home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / nsUrlClassifierLib.js < prev    next >
Text File  |  2007-10-18  |  136KB  |  4,177 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Url Classifier code
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Google Inc.
  18.  * Portions created by the Initial Developer are Copyright (C) 2006
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Tony Chang <tony@ponderer.org>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. // We wastefully reload the same JS files across components.  This puts all
  39. // the common JS files used by safebrowsing and url-classifier into a
  40. // single component.
  41.  
  42. const Cc = Components.classes;
  43. const Ci = Components.interfaces;
  44. const G_GDEBUG = false;
  45.  
  46. // TODO: get rid of application.js and filesystem.js (not used much)
  47. /* ***** BEGIN LICENSE BLOCK *****
  48.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  49.  *
  50.  * The contents of this file are subject to the Mozilla Public License Version
  51.  * 1.1 (the "License"); you may not use this file except in compliance with
  52.  * the License. You may obtain a copy of the License at
  53.  * http://www.mozilla.org/MPL/
  54.  *
  55.  * Software distributed under the License is distributed on an "AS IS" basis,
  56.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  57.  * for the specific language governing rights and limitations under the
  58.  * License.
  59.  *
  60.  * The Original Code is Google Safe Browsing.
  61.  *
  62.  * The Initial Developer of the Original Code is Google Inc.
  63.  * Portions created by the Initial Developer are Copyright (C) 2006
  64.  * the Initial Developer. All Rights Reserved.
  65.  *
  66.  * Contributor(s):
  67.  *   Aaron Boodman <aa@google.com> (original author)
  68.  *
  69.  * Alternatively, the contents of this file may be used under the terms of
  70.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  71.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  72.  * in which case the provisions of the GPL or the LGPL are applicable instead
  73.  * of those above. If you wish to allow use of your version of this file only
  74.  * under the terms of either the GPL or the LGPL, and not to allow others to
  75.  * use your version of this file under the terms of the MPL, indicate your
  76.  * decision by deleting the provisions above and replace them with the notice
  77.  * and other provisions required by the GPL or the LGPL. If you do not delete
  78.  * the provisions above, a recipient may use your version of this file under
  79.  * the terms of any one of the MPL, the GPL or the LGPL.
  80.  *
  81.  * ***** END LICENSE BLOCK ***** */
  82.  
  83. // This file has pure js helper functions. Hence you'll find metion
  84. // of browser-specific features in here.
  85.  
  86.  
  87. /**
  88.  * lang.js - The missing JavaScript language features
  89.  *
  90.  * WARNING: This class adds members to the prototypes of String, Array, and
  91.  * Function for convenience.
  92.  *
  93.  * The tradeoff is that the for/in statement will not work properly for those
  94.  * objects when this library is used.
  95.  *
  96.  * To work around this for Arrays, you may want to use the forEach() method,
  97.  * which is more fun and easier to read.
  98.  */
  99.  
  100. /**
  101.  * Returns true if the specified value is |null|
  102.  */
  103. function isNull(val) {
  104.   return val === null;
  105. }
  106.  
  107. /**
  108.  * Returns true if the specified value is an array
  109.  */
  110. function isArray(val) {
  111.   return isObject(val) && val.constructor == Array;
  112. }
  113.  
  114. /**
  115.  * Returns true if the specified value is a string
  116.  */
  117. function isString(val) {
  118.   return typeof val == "string";
  119. }
  120.  
  121. /**
  122.  * Returns true if the specified value is a boolean
  123.  */
  124. function isBoolean(val) {
  125.   return typeof val == "boolean";
  126. }
  127.  
  128. /**
  129.  * Returns true if the specified value is a number
  130.  */
  131. function isNumber(val) {
  132.   return typeof val == "number";
  133. }
  134.  
  135. /**
  136.  * Returns true if the specified value is a function
  137.  */
  138. function isFunction(val) {
  139.   return typeof val == "function";
  140. }
  141.  
  142. /**
  143.  * Returns true if the specified value is an object
  144.  */
  145. function isObject(val) {
  146.   return val && typeof val == "object";
  147. }
  148.  
  149. /**
  150.  * Returns an array of all the properties defined on an object
  151.  */
  152. function getObjectProps(obj) {
  153.   var ret = [];
  154.  
  155.   for (var p in obj) {
  156.     ret.push(p);
  157.   }
  158.  
  159.   return ret;
  160. }
  161.  
  162. /**
  163.  * Returns true if the specified value is an object which has no properties
  164.  * defined.
  165.  */
  166. function isEmptyObject(val) {
  167.   if (!isObject(val)) {
  168.     return false;
  169.   }
  170.  
  171.   for (var p in val) {
  172.     return false;
  173.   }
  174.  
  175.   return true;
  176. }
  177.  
  178. var getHashCode;
  179. var removeHashCode;
  180.  
  181. (function () {
  182.   var hashCodeProperty = "lang_hashCode_";
  183.  
  184.   /**
  185.    * Adds a lang_hashCode_ field to an object. The hash code is unique for the
  186.    * given object.
  187.    * @param obj {Object} The object to get the hash code for
  188.    * @returns {Number} The hash code for the object
  189.    */
  190.   getHashCode = function(obj) {
  191.     // In IE, DOM nodes do not extend Object so they do not have this method.
  192.     // we need to check hasOwnProperty because the proto might have this set.
  193.     if (obj.hasOwnProperty && obj.hasOwnProperty(hashCodeProperty)) {
  194.       return obj[hashCodeProperty];
  195.     }
  196.     if (!obj[hashCodeProperty]) {
  197.       obj[hashCodeProperty] = ++getHashCode.hashCodeCounter_;
  198.     }
  199.     return obj[hashCodeProperty];
  200.   };
  201.  
  202.   /**
  203.    * Removes the lang_hashCode_ field from an object.
  204.    * @param obj {Object} The object to remove the field from. 
  205.    */
  206.   removeHashCode = function(obj) {
  207.     obj.removeAttribute(hashCodeProperty);
  208.   };
  209.  
  210.   getHashCode.hashCodeCounter_ = 0;
  211. })();
  212.  
  213. /**
  214.  * Fast prefix-checker.
  215.  */
  216. String.prototype.startsWith = function(prefix) {
  217.   if (this.length < prefix.length) {
  218.     return false;
  219.   }
  220.  
  221.   if (this.substring(0, prefix.length) == prefix) {
  222.     return true;
  223.   }
  224.  
  225.   return false;
  226. }
  227.  
  228. /**
  229.  * Removes whitespace from the beginning and end of the string
  230.  */
  231. String.prototype.trim = function() {
  232.   return this.replace(/^\s+|\s+$/g, "");
  233. }
  234.  
  235. /**
  236.  * Does simple python-style string substitution.
  237.  * "foo%s hot%s".subs("bar", "dog") becomes "foobar hotdot".
  238.  * For more fully-featured templating, see template.js.
  239.  */
  240. String.prototype.subs = function() {
  241.   var ret = this;
  242.  
  243.   // this appears to be slow, but testing shows it compares more or less equiv.
  244.   // to the regex.exec method.
  245.   for (var i = 0; i < arguments.length; i++) {
  246.     ret = ret.replace(/\%s/, String(arguments[i]));
  247.   }
  248.  
  249.   return ret;
  250. }
  251.  
  252. /**
  253.  * Returns the last element on an array without removing it.
  254.  */
  255. Array.prototype.peek = function() {
  256.   return this[this.length - 1];
  257. }
  258.  
  259. // TODO(anyone): add splice the first time someone needs it and then implement
  260. // push, pop, shift, unshift in terms of it where possible.
  261.  
  262. // TODO(anyone): add the other neat-o functional methods like map(), etc.
  263.  
  264. /**
  265.  * Partially applies this function to a particular "this object" and zero or
  266.  * more arguments. The result is a new function with some arguments of the first
  267.  * function pre-filled and the value of |this| "pre-specified".
  268.  *
  269.  * Remaining arguments specified at call-time are appended to the pre-
  270.  * specified ones.
  271.  *
  272.  * Also see: partial().
  273.  *
  274.  * Note that bind and partial are optimized such that repeated calls to it do 
  275.  * not create more than one function object, so there is no additional cost for
  276.  * something like:
  277.  *
  278.  * var g = bind(f, obj);
  279.  * var h = partial(g, 1, 2, 3);
  280.  * var k = partial(h, a, b, c);
  281.  *
  282.  * Usage:
  283.  * var barMethBound = bind(myFunction, myObj, "arg1", "arg2");
  284.  * barMethBound("arg3", "arg4");
  285.  *
  286.  * @param thisObj {object} Specifies the object which |this| should point to
  287.  * when the function is run. If the value is null or undefined, it will default
  288.  * to the global object.
  289.  *
  290.  * @returns {function} A partially-applied form of the function bind() was
  291.  * invoked as a method of.
  292.  */
  293. function bind(fn, self, opt_args) {
  294.   var boundargs = (typeof fn.boundArgs_ != "undefined") ? fn.boundArgs_ : [];
  295.   boundargs = boundargs.concat(Array.prototype.slice.call(arguments, 2));
  296.  
  297.   if (typeof fn.boundSelf_ != "undefined") {
  298.     self = fn.boundSelf_;
  299.   }
  300.  
  301.   if (typeof fn.boundFn_ != "undefined") {
  302.     fn = fn.boundFn_;
  303.   }
  304.  
  305.   var newfn = function() {
  306.     // Combine the static args and the new args into one big array
  307.     var args = boundargs.concat(Array.prototype.slice.call(arguments));
  308.     return fn.apply(self, args);
  309.   }
  310.  
  311.   newfn.boundArgs_ = boundargs;
  312.   newfn.boundSelf_ = self;
  313.   newfn.boundFn_ = fn;
  314.  
  315.   return newfn;
  316. }
  317.  
  318. /**
  319.  * An alias to the bind() global function.
  320.  *
  321.  * Usage:
  322.  * var g = f.bind(obj, arg1, arg2);
  323.  * g(arg3, arg4);
  324.  */
  325. Function.prototype.bind = function(self, opt_args) {
  326.   return bind.apply(
  327.     null, [this, self].concat(Array.prototype.slice.call(arguments, 1)));
  328. }
  329.  
  330. /**
  331.  * Like bind(), except that a "this object" is not required. Useful when the
  332.  * target function is already bound.
  333.  * 
  334.  * Usage:
  335.  * var g = partial(f, arg1, arg2);
  336.  * g(arg3, arg4);
  337.  */
  338. function partial(fn, opt_args) {
  339.   return bind.apply(
  340.     null, [fn, null].concat(Array.prototype.slice.call(arguments, 1)));
  341. }
  342.  
  343. /**
  344.  * An alias to the partial() global function.
  345.  *
  346.  * Usage:
  347.  * var g = f.partial(arg1, arg2);
  348.  * g(arg3, arg4);
  349.  */
  350. Function.prototype.partial = function(opt_args) {
  351.   return bind.apply(
  352.     null, [this, null].concat(Array.prototype.slice.call(arguments)));
  353. }
  354.  
  355. /**
  356.  * Convenience. Binds all the methods of obj to itself. Calling this in the
  357.  * constructor before referencing any methods makes things a little more like
  358.  * Java or Python where methods are intrinsically bound to their instance.
  359.  */
  360. function bindMethods(obj) {
  361.   for (var p in obj) {
  362.     if (isFunction(obj[p])) {
  363.       obj[p] = obj[p].bind(obj);
  364.     }
  365.   }
  366. }
  367.  
  368. /**
  369.  * Inherit the prototype methods from one constructor into another.
  370.  *
  371.  * Usage:
  372.  * <pre>
  373.  * function ParentClass(a, b) { }
  374.  * ParentClass.prototype.foo = function(a) { }
  375.  *
  376.  * function ChildClass(a, b, c) {
  377.  *   ParentClass.call(this, a, b);
  378.  * }
  379.  *
  380.  * ChildClass.inherits(ParentClass);
  381.  *
  382.  * var child = new ChildClass("a", "b", "see");
  383.  * child.foo(); // works
  384.  * </pre>
  385.  *
  386.  * In addition, a superclass' implementation of a method can be invoked
  387.  * as follows:
  388.  *
  389.  * <pre>
  390.  * ChildClass.prototype.foo = function(a) {
  391.  *   ChildClass.superClass_.foo.call(this, a);
  392.  *   // other code
  393.  * };
  394.  * </pre>
  395.  */
  396. Function.prototype.inherits = function(parentCtor) {
  397.   var tempCtor = function(){};
  398.   tempCtor.prototype = parentCtor.prototype;
  399.   this.superClass_ = parentCtor.prototype;
  400.   this.prototype = new tempCtor();
  401. }
  402. //@line 48 "/cygdrive/K/tinderbuild/src/flock/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"
  403.  
  404. /* ***** BEGIN LICENSE BLOCK *****
  405.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  406.  *
  407.  * The contents of this file are subject to the Mozilla Public License Version
  408.  * 1.1 (the "License"); you may not use this file except in compliance with
  409.  * the License. You may obtain a copy of the License at
  410.  * http://www.mozilla.org/MPL/
  411.  *
  412.  * Software distributed under the License is distributed on an "AS IS" basis,
  413.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  414.  * for the specific language governing rights and limitations under the
  415.  * License.
  416.  *
  417.  * The Original Code is Google Safe Browsing.
  418.  *
  419.  * The Initial Developer of the Original Code is Google Inc.
  420.  * Portions created by the Initial Developer are Copyright (C) 2006
  421.  * the Initial Developer. All Rights Reserved.
  422.  *
  423.  * Contributor(s):
  424.  *   Fritz Schneider <fritz@google.com> (original author)
  425.  *
  426.  * Alternatively, the contents of this file may be used under the terms of
  427.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  428.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  429.  * in which case the provisions of the GPL or the LGPL are applicable instead
  430.  * of those above. If you wish to allow use of your version of this file only
  431.  * under the terms of either the GPL or the LGPL, and not to allow others to
  432.  * use your version of this file under the terms of the MPL, indicate your
  433.  * decision by deleting the provisions above and replace them with the notice
  434.  * and other provisions required by the GPL or the LGPL. If you do not delete
  435.  * the provisions above, a recipient may use your version of this file under
  436.  * the terms of any one of the MPL, the GPL or the LGPL.
  437.  *
  438.  * ***** END LICENSE BLOCK ***** */
  439.  
  440.  
  441. // Class for manipulating preferences. Aside from wrapping the pref
  442. // service, useful functionality includes:
  443. //
  444. // - abstracting prefobserving so that you can observe preferences
  445. //   without implementing nsIObserver 
  446. // 
  447. // - getters that return a default value when the pref doesn't exist 
  448. //   (instead of throwing)
  449. // 
  450. // - get-and-set getters
  451. //
  452. // Example:
  453. // 
  454. // var p = new PROT_Preferences();
  455. // alert(p.getPref("some-true-pref"));     // shows true
  456. // alert(p.getPref("no-such-pref", true)); // shows true   
  457. // alert(p.getPref("no-such-pref", null)); // shows null
  458. //
  459. // function observe(prefThatChanged) {
  460. //   alert("Pref changed: " + prefThatChanged);
  461. // };
  462. //
  463. // p.addObserver("somepref", observe);
  464. // p.setPref("somepref", true);            // alerts
  465. // p.removeObserver("somepref", observe);
  466. //
  467. // TODO: should probably have the prefobserver pass in the new and old
  468. //       values
  469.  
  470. // TODO(tc): Maybe remove this class and just call natively since we're no
  471. //           longer an extension.
  472.  
  473. /**
  474.  * A class that wraps the preferences service.
  475.  *
  476.  * @param opt_startPoint        A starting point on the prefs tree to resolve 
  477.  *                              names passed to setPref and getPref.
  478.  *
  479.  * @param opt_useDefaultPranch  Set to true to work against the default 
  480.  *                              preferences tree instead of the profile one.
  481.  *
  482.  * @constructor
  483.  */
  484. function G_Preferences(opt_startPoint, opt_getDefaultBranch) {
  485.   this.debugZone = "prefs";
  486.   this.observers_ = {};
  487.   this.getDefaultBranch_ = !!opt_getDefaultBranch;
  488.  
  489.   this.startPoint_ = opt_startPoint || null;
  490. }
  491.  
  492. G_Preferences.setterMap_ = { "string": "setCharPref",
  493.                              "boolean": "setBoolPref",
  494.                              "number": "setIntPref" };
  495.  
  496. G_Preferences.getterMap_ = {};
  497. G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref";
  498. G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref";
  499. G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_INT] = "getIntPref";
  500.  
  501. G_Preferences.prototype.__defineGetter__('prefs_', function() {
  502.   var prefs;
  503.   var prefSvc = Cc["@mozilla.org/preferences-service;1"]
  504.                   .getService(Ci.nsIPrefService);
  505.  
  506.   if (this.getDefaultBranch_) {
  507.     prefs = prefSvc.getDefaultBranch(this.startPoint_);
  508.   } else {
  509.     prefs = prefSvc.getBranch(this.startPoint_);
  510.   }
  511.  
  512.   // QI to prefs in case we want to add observers
  513.   prefs.QueryInterface(Ci.nsIPrefBranchInternal);
  514.   return prefs;
  515. });
  516.  
  517. /**
  518.  * Stores a key/value in a user preference. Valid types for val are string,
  519.  * boolean, and number. Complex values are not yet supported (but feel free to
  520.  * add them!).
  521.  */
  522. G_Preferences.prototype.setPref = function(key, val) {
  523.   var datatype = typeof(val);
  524.  
  525.   if (datatype == "number" && (val % 1 != 0)) {
  526.     throw new Error("Cannot store non-integer numbers in preferences.");
  527.   }
  528.  
  529.   var meth = G_Preferences.setterMap_[datatype];
  530.  
  531.   if (!meth) {
  532.     throw new Error("Pref datatype {" + datatype + "} not supported.");
  533.   }
  534.  
  535.   return this.prefs_[meth](key, val);
  536. }
  537.  
  538. /**
  539.  * Retrieves a user preference. Valid types for the value are the same as for
  540.  * setPref. If the preference is not found, opt_default will be returned 
  541.  * instead.
  542.  */
  543. G_Preferences.prototype.getPref = function(key, opt_default) {
  544.   var type = this.prefs_.getPrefType(key);
  545.  
  546.   // zero means that the specified pref didn't exist
  547.   if (type == Ci.nsIPrefBranch.PREF_INVALID) {
  548.     return opt_default;
  549.   }
  550.  
  551.   var meth = G_Preferences.getterMap_[type];
  552.  
  553.   if (!meth) {
  554.     throw new Error("Pref datatype {" + type + "} not supported.");
  555.   }
  556.  
  557.   // If a pref has been cleared, it will have a valid type but won't
  558.   // be gettable, so this will throw.
  559.   try {
  560.     return this.prefs_[meth](key);
  561.   } catch(e) {
  562.     return opt_default;
  563.   }
  564. }
  565.  
  566. /**
  567.  * Set a boolean preference
  568.  *
  569.  * @param which Name of preference to set
  570.  * @param value Boolean indicating value to set
  571.  *
  572.  * @deprecated  Just use setPref.
  573.  */
  574. G_Preferences.prototype.setBoolPref = function(which, value) {
  575.   return this.setPref(which, value);
  576. }
  577.  
  578. /**
  579.  * Get a boolean preference. WILL THROW IF PREFERENCE DOES NOT EXIST.
  580.  * If you don't want this behavior, use getBoolPrefOrDefault.
  581.  *
  582.  * @param which Name of preference to get.
  583.  *
  584.  * @deprecated  Just use getPref.
  585.  */
  586. G_Preferences.prototype.getBoolPref = function(which) {
  587.   return this.prefs_.getBoolPref(which);
  588. }
  589.  
  590. /**
  591.  * Get a boolean preference or return some default value if it doesn't
  592.  * exist. Note that the default doesn't have to be bool -- it could be
  593.  * anything (e.g., you could pass in null and check if the return
  594.  * value is === null to determine if the pref doesn't exist).
  595.  *
  596.  * @param which Name of preference to get.
  597.  * @param def Value to return if the preference doesn't exist
  598.  * @returns Boolean value of the pref if it exists, else def
  599.  *
  600.  * @deprecated  Just use getPref.
  601.  */
  602. G_Preferences.prototype.getBoolPrefOrDefault = function(which, def) {
  603.   return this.getPref(which, def);
  604. }
  605.  
  606. /**
  607.  * Get a boolean preference if it exists. If it doesn't, set its value
  608.  * to a default and return the default. Note that the default will be
  609.  * coherced to a bool if it is set, but not in the return value.
  610.  *
  611.  * @param which Name of preference to get.
  612.  * @param def Value to set and return if the preference doesn't exist
  613.  * @returns Boolean value of the pref if it exists, else def
  614.  *
  615.  * @deprecated  Just use getPref.
  616.  */
  617. G_Preferences.prototype.getBoolPrefOrDefaultAndSet = function(which, def) {
  618.   try {
  619.     return this.prefs_.getBoolPref(which);
  620.   } catch(e) {
  621.     this.prefs_.setBoolPref(which, !!def);  // The !! forces boolean conversion
  622.     return def;
  623.   }
  624. }
  625.  
  626. /**
  627.  * Delete a preference. 
  628.  *
  629.  * @param which Name of preference to obliterate
  630.  */
  631. G_Preferences.prototype.clearPref = function(which) {
  632.   try {
  633.     // This throws if the pref doesn't exist, which is fine because a 
  634.     // non-existent pref is cleared
  635.     this.prefs_.clearUserPref(which);
  636.   } catch(e) {}
  637. }
  638.  
  639. /**
  640.  * Add an observer for a given pref.
  641.  *
  642.  * @param which String containing the pref to listen to
  643.  * @param callback Function to be called when the pref changes. This
  644.  *                 function will receive a single argument, a string 
  645.  *                 holding the preference name that changed
  646.  */
  647. G_Preferences.prototype.addObserver = function(which, callback) {
  648.   var observer = new G_PreferenceObserver(callback);
  649.   // Need to store the observer we create so we can eventually unregister it
  650.   if (!this.observers_[which])
  651.     this.observers_[which] = new G_ObjectSafeMap();
  652.   this.observers_[which].insert(callback, observer);
  653.   this.prefs_.addObserver(which, observer, false /* strong reference */);
  654. }
  655.  
  656. /**
  657.  * Remove an observer for a given pref.
  658.  *
  659.  * @param which String containing the pref to stop listening to
  660.  * @param callback Function to remove as an observer
  661.  */
  662. G_Preferences.prototype.removeObserver = function(which, callback) {
  663.   var observer = this.observers_[which].find(callback);
  664.   G_Assert(this, !!observer, "Tried to unregister a nonexistant observer"); 
  665.   this.prefs_.removeObserver(which, observer);
  666.   this.observers_[which].erase(callback);
  667. }
  668.  
  669.  
  670. /**
  671.  * Helper class that knows how to observe preference changes and
  672.  * invoke a callback when they do
  673.  *
  674.  * @constructor
  675.  * @param callback Function to call when the preference changes
  676.  */
  677. function G_PreferenceObserver(callback) {
  678.   this.debugZone = "prefobserver";
  679.   this.callback_ = callback;
  680. }
  681.  
  682. /**
  683.  * Invoked by the pref system when a preference changes. Passes the
  684.  * message along to the callback.
  685.  *
  686.  * @param subject The nsIPrefBranch that changed
  687.  * @param topic String "nsPref:changed" (aka 
  688.  *              NS_PREFBRANCH_PREFCHANGE_OBSERVER_ID -- but where does it
  689.  *              live???)
  690.  * @param data Name of the pref that changed
  691.  */
  692. G_PreferenceObserver.prototype.observe = function(subject, topic, data) {
  693.   G_Debug(this, "Observed pref change: " + data);
  694.   this.callback_(data);
  695. }
  696.  
  697. /**
  698.  * XPCOM cruft
  699.  *
  700.  * @param iid Interface id of the interface the caller wants
  701.  */
  702. G_PreferenceObserver.prototype.QueryInterface = function(iid) {
  703.   var Ci = Ci;
  704.   if (iid.equals(Ci.nsISupports) || 
  705.       iid.equals(Ci.nsIObserves) ||
  706.       iid.equals(Ci.nsISupportsWeakReference))
  707.     return this;
  708.   throw Components.results.NS_ERROR_NO_INTERFACE;
  709. }
  710.  
  711. /* ***** BEGIN LICENSE BLOCK *****
  712.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  713.  *
  714.  * The contents of this file are subject to the Mozilla Public License Version
  715.  * 1.1 (the "License"); you may not use this file except in compliance with
  716.  * the License. You may obtain a copy of the License at
  717.  * http://www.mozilla.org/MPL/
  718.  *
  719.  * Software distributed under the License is distributed on an "AS IS" basis,
  720.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  721.  * for the specific language governing rights and limitations under the
  722.  * License.
  723.  *
  724.  * The Original Code is Google Safe Browsing.
  725.  *
  726.  * The Initial Developer of the Original Code is Google Inc.
  727.  * Portions created by the Initial Developer are Copyright (C) 2006
  728.  * the Initial Developer. All Rights Reserved.
  729.  *
  730.  * Contributor(s):
  731.  *   Aaron Boodman <aa@google.com> (original author)
  732.  *   Raphael Moll <raphael@google.com>
  733.  *   Fritz Schneider <fritz@google.com>
  734.  *
  735.  * Alternatively, the contents of this file may be used under the terms of
  736.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  737.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  738.  * in which case the provisions of the GPL or the LGPL are applicable instead
  739.  * of those above. If you wish to allow use of your version of this file only
  740.  * under the terms of either the GPL or the LGPL, and not to allow others to
  741.  * use your version of this file under the terms of the MPL, indicate your
  742.  * decision by deleting the provisions above and replace them with the notice
  743.  * and other provisions required by the GPL or the LGPL. If you do not delete
  744.  * the provisions above, a recipient may use your version of this file under
  745.  * the terms of any one of the MPL, the GPL or the LGPL.
  746.  *
  747.  * ***** END LICENSE BLOCK ***** */
  748.  
  749.  
  750. // Utilities for working with nsIFile and related interfaces.
  751.  
  752. /**
  753.  * Stub for an nsIFile wrapper which doesn't exist yet. Perhaps in the future
  754.  * we could add functionality to nsILocalFile which would be useful to us here,
  755.  * but for now, no need for such. This could be done by setting 
  756.  * __proto__ to an instance of nsIFile, for example. Neat.
  757.  */
  758. var G_File = {};
  759.  
  760. /**
  761.  * Returns an nsIFile pointing to the user's home directory, or optionally, a
  762.  * file inside that dir.
  763.  */
  764. G_File.getHomeFile = function(opt_file) {
  765.   return this.getSpecialFile("Home", opt_file);
  766. }
  767.  
  768. /**
  769.  * Returns an nsIFile pointing to the current profile folder, or optionally, a
  770.  * file inside that dir.
  771.  */
  772. G_File.getProfileFile = function(opt_file) {
  773.   return this.getSpecialFile("ProfD", opt_file);
  774. }
  775.  
  776. /**
  777.  * returns an nsIFile pointing to the temporary dir, or optionally, a file 
  778.  * inside that dir.
  779.  */
  780. G_File.getTempFile = function(opt_file) {
  781.   return this.getSpecialFile("TmpD", opt_file);
  782. }
  783.  
  784. /**
  785.  * Returns an nsIFile pointing to one of the special named directories defined 
  786.  * by Firefox, such as the user's home directory, the profile directory, etc. 
  787.  * 
  788.  * As a convenience, callers may specify the opt_file argument to get that file
  789.  * within the special directory instead.
  790.  *
  791.  * http://lxr.mozilla.org/seamonkey/source/xpcom/io/nsDirectoryServiceDefs.h
  792.  * http://kb.mozillazine.org/File_IO#Getting_special_files
  793.  */
  794. G_File.getSpecialFile = function(loc, opt_file) {
  795.   var file = Cc["@mozilla.org/file/directory_service;1"]
  796.              .getService(Ci.nsIProperties)
  797.              .get(loc, Ci.nsILocalFile);
  798.  
  799.   if (opt_file) {
  800.     file.append(opt_file);
  801.   }
  802.  
  803.   return file;
  804. }
  805.  
  806. /**
  807.  * Creates and returns a pointer to a unique file in the temporary directory
  808.  * with an optional base name.
  809.  */
  810. G_File.createUniqueTempFile = function(opt_baseName) {
  811.   var baseName = (opt_baseName || (new Date().getTime())) + ".tmp";
  812.  
  813.   var file = this.getSpecialFile("TmpD", baseName);
  814.   file.createUnique(file.NORMAL_FILE_TYPE, 0644);
  815.  
  816.   return file;
  817. }
  818.  
  819. /**
  820.  * Creates and returns a pointer to a unique temporary directory, with
  821.  * an optional base name.
  822.  */
  823. G_File.createUniqueTempDir = function(opt_baseName) {
  824.   var baseName = (opt_baseName || (new Date().getTime())) + ".tmp";
  825.  
  826.   var dir = this.getSpecialFile("TmpD", baseName);
  827.   dir.createUnique(dir.DIRECTORY_TYPE, 0744);
  828.  
  829.   return dir;
  830. }
  831.  
  832. /**
  833.  * Static method to retrieve an nsIFile from a file:// URI.
  834.  */
  835. G_File.fromFileURI = function(uri) {
  836.   // Ensure they use file:// url's: discourages platform-specific paths
  837.   if (uri.indexOf("file://") != 0)
  838.     throw new Error("File path must be a file:// URL");
  839.  
  840.   var fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"]
  841.                     .getService(Ci.nsIFileProtocolHandler);
  842.   return fileHandler.getFileFromURLSpec(uri);
  843. }
  844.  
  845. // IO Constants
  846.  
  847. G_File.PR_RDONLY = 0x01;      // read-only
  848. G_File.PR_WRONLY = 0x02;      // write only
  849. G_File.PR_RDWR = 0x04;        // reading and writing
  850. G_File.PR_CREATE_FILE = 0x08; // create if it doesn't exist
  851. G_File.PR_APPEND = 0x10;      // file pntr reset to end prior to writes
  852. G_File.PR_TRUNCATE = 0x20;    // file exists its length is set to zero
  853. G_File.PR_SYNC = 0x40;        // writes wait for data to be physically written
  854. G_File.PR_EXCL = 0x80;        // file does not exist ? created : no action
  855.  
  856. // The character(s) to use for line-endings, which are platform-specific.
  857. // This doesn't work for mac os9, but I don't know of a good way to detect
  858. // OS9-ness from JS.
  859. G_File.__defineGetter__("LINE_END_CHAR", function() {
  860.   var end_char = Cc["@mozilla.org/xre/app-info;1"]
  861.                  .getService(Ci.nsIXULRuntime)
  862.                  .OS == "WINNT" ? "\r\n" : "\n";
  863.  
  864.   // Cache result
  865.   G_File.__defineGetter__("LINE_END_CHAR", function() { return end_char; });
  866.   return end_char;
  867. });
  868.  
  869. /**
  870.  * A class which can read a file incrementally or all at once. Parameter can be
  871.  * either an nsIFile instance or a string file:// URI.
  872.  * Note that this class is not compatible with non-ascii data.
  873.  */
  874. function G_FileReader(file) {
  875.   this.file_ = isString(file) ? G_File.fromFileURI(file) : file;
  876. }
  877.  
  878. /**
  879.  * Utility method to read the entire contents of a file. Parameter can be either
  880.  * an nsIFile instance or a string file:// URI.
  881.  */
  882. G_FileReader.readAll = function(file) { 
  883.   var reader = new G_FileReader(file);
  884.  
  885.   try {
  886.     return reader.read();
  887.   } finally {
  888.     reader.close();
  889.   }
  890. }
  891.  
  892. /**
  893.  * Read up to opt_maxBytes from the stream. If opt_maxBytes is not specified, 
  894.  * the entire file is read.
  895.  */
  896. G_FileReader.prototype.read = function(opt_maxBytes) {
  897.   if (!this.stream_) {
  898.     var fs = Cc["@mozilla.org/network/file-input-stream;1"]
  899.                .createInstance(Ci.nsIFileInputStream);
  900.     fs.init(this.file_, G_File.PR_RDONLY, 0444, 0);
  901.  
  902.     this.stream_ = Cc["@mozilla.org/scriptableinputstream;1"]
  903.                      .createInstance(Ci.nsIScriptableInputStream);
  904.     this.stream_.init(fs);
  905.   }
  906.   
  907.   if (typeof opt_maxBytes == "undefined") {
  908.     opt_maxBytes = this.stream_.available();
  909.   }
  910.  
  911.   return this.stream_.read(opt_maxBytes);
  912. }
  913.  
  914. /**
  915.  * Close the stream. This step is required when reading is done.
  916.  */
  917. G_FileReader.prototype.close = function(opt_maxBytes) {
  918.   if (this.stream_) {
  919.     this.stream_.close();
  920.     this.stream_ = null;
  921.   }
  922. }
  923.  
  924.  
  925. // TODO(anyone): Implement G_LineReader. The interface should be something like:
  926. // for (var line = null; line = reader.readLine();) {
  927. //   // do something with line
  928. // }
  929.  
  930.  
  931. /**
  932.  * Writes a file incrementally or all at once.
  933.  * Note that this class is not compatible with non-ascii data.
  934.  */
  935. function G_FileWriter(file, opt_append) {
  936.   this.file_ = typeof file == "string" ? G_File.fromFileURI(file) : file;
  937.   this.append_ = !!opt_append;
  938. }
  939.  
  940. /**
  941.  * Helper to write to a file in one step.
  942.  */
  943. G_FileWriter.writeAll = function(file, data, opt_append) { 
  944.   var writer = new G_FileWriter(file, opt_append);
  945.  
  946.   try {
  947.     return writer.write(data);
  948.   } finally {
  949.     writer.close();
  950.     return 0;
  951.   }
  952. }
  953.  
  954. /**
  955.  * Write bytes out to the file. Returns the number of bytes written.
  956.  */
  957. G_FileWriter.prototype.write = function(data) {
  958.   if (!this.stream_) {
  959.     this.stream_ = Cc["@mozilla.org/network/file-output-stream;1"]
  960.                      .createInstance(Ci.nsIFileOutputStream);
  961.  
  962.     var flags = G_File.PR_WRONLY | 
  963.                 G_File.PR_CREATE_FILE | 
  964.                 (this.append_ ? G_File.PR_APPEND : G_File.PR_TRUNCATE);
  965.  
  966.  
  967.     this.stream_.init(this.file_, 
  968.                       flags, 
  969.                       -1 /* default perms */, 
  970.                       0 /* no special behavior */);
  971.   }
  972.  
  973.   return this.stream_.write(data, data.length);
  974. }
  975.  
  976. /**
  977.  * Writes bytes out to file followed by the approriate line-ending character for
  978.  * the current platform.
  979.  */
  980. G_FileWriter.prototype.writeLine = function(data) {
  981.   this.write(data + G_File.LINE_END_CHAR);
  982. }
  983.  
  984.  
  985. /**
  986.  * Closes the file. This must becalled when writing is done.
  987.  */
  988. G_FileWriter.prototype.close = function() {
  989.   if (this.stream_) {
  990.     this.stream_.close();
  991.     this.stream_ = null;
  992.   }
  993. }
  994.  
  995. /* ***** BEGIN LICENSE BLOCK *****
  996.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  997.  *
  998.  * The contents of this file are subject to the Mozilla Public License Version
  999.  * 1.1 (the "License"); you may not use this file except in compliance with
  1000.  * the License. You may obtain a copy of the License at
  1001.  * http://www.mozilla.org/MPL/
  1002.  *
  1003.  * Software distributed under the License is distributed on an "AS IS" basis,
  1004.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  1005.  * for the specific language governing rights and limitations under the
  1006.  * License.
  1007.  *
  1008.  * The Original Code is Google Safe Browsing.
  1009.  *
  1010.  * The Initial Developer of the Original Code is Google Inc.
  1011.  * Portions created by the Initial Developer are Copyright (C) 2006
  1012.  * the Initial Developer. All Rights Reserved.
  1013.  *
  1014.  * Contributor(s):
  1015.  *   Fritz Schneider <fritz@google.com> (original author)
  1016.  *   Annie Sullivan <sullivan@google.com>
  1017.  *   Aaron Boodman <aa@google.com>
  1018.  *
  1019.  * Alternatively, the contents of this file may be used under the terms of
  1020.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  1021.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1022.  * in which case the provisions of the GPL or the LGPL are applicable instead
  1023.  * of those above. If you wish to allow use of your version of this file only
  1024.  * under the terms of either the GPL or the LGPL, and not to allow others to
  1025.  * use your version of this file under the terms of the MPL, indicate your
  1026.  * decision by deleting the provisions above and replace them with the notice
  1027.  * and other provisions required by the GPL or the LGPL. If you do not delete
  1028.  * the provisions above, a recipient may use your version of this file under
  1029.  * the terms of any one of the MPL, the GPL or the LGPL.
  1030.  *
  1031.  * ***** END LICENSE BLOCK ***** */
  1032.  
  1033. // Generic logging/debugging functionality that:
  1034. //
  1035. // (*) when disabled compiles to no-ops at worst (for calls to the service) 
  1036. //     and to nothing at best (calls to G_Debug() and similar are compiled
  1037. //     away when you use a jscompiler that strips dead code)
  1038. //
  1039. // (*) has dynamically configurable/creatable debugging "zones" enabling 
  1040. //     selective logging
  1041. // 
  1042. // (*) hides its plumbing so that all calls in different zones are uniform,
  1043. //     so you can drop files using this library into other apps that use it
  1044. //     without any configuration 
  1045. //
  1046. // (*) can be controlled programmatically or via preferences.  The
  1047. //     preferences that control the service and its zones are under
  1048. //     the preference branch "safebrowsing-debug-service."
  1049. //
  1050. // (*) outputs function call traces when the "loggifier" zone is enabled
  1051. // 
  1052. // (*) can write output to logfiles so that you can get a call trace
  1053. //     from someone who is having a problem
  1054. //
  1055. // Example:
  1056. //
  1057. // var G_GDEBUG = true                           // Enable this module
  1058. // var G_debugService = new G_DebugService();    // in global context
  1059. //
  1060. // // You can use it with arbitrary primitive first arguement
  1061. // G_Debug("myzone", "Yo yo yo");   // outputs: [myzone] Yo yo yo\n
  1062. //
  1063. // // But it's nice to use it with an object; it will probe for the zone name
  1064. // function Obj() {
  1065. //   this.debugZone = "someobj";
  1066. // }
  1067. // Obj.prototype.foo = function() {
  1068. //   G_Debug(this, "foo called");
  1069. // }
  1070. // (new Obj).foo();                        // outputs: [someobj] foo called\n
  1071. //
  1072. // G_debugService.loggifier.loggify(Obj.prototype);  // enable call tracing
  1073. //
  1074. // // En/disable specific zones programmatically (you can also use preferences)
  1075. // G_debugService.enableZone("somezone");
  1076. // G_debugService.disableZone("someotherzone");
  1077. // G_debugService.enableAllZones();
  1078. //
  1079. // // We also have asserts and errors:
  1080. // G_Error(this, "Some error occurred");                    // will throw
  1081. // G_Assert(this, (x > 3), "x not greater than three!");    // will throw
  1082. //
  1083. // See classes below for more methods. 
  1084. //
  1085. // TODO add abililty to alert() instead of dump()? Should be easy.
  1086. // TODO add code to set prefs when not found to the default value of a tristate
  1087. // TODO add error level support
  1088. // TODO add ability to turn off console output
  1089. //
  1090. // -------> TO START DEBUGGING: set G_GDEBUG to true
  1091.  
  1092. // These are the functions code will typically call. Everything is
  1093. // wrapped in if's so we can compile it away when G_GDEBUG is false.
  1094.  
  1095.  
  1096. if (typeof G_GDEBUG == "undefined") {
  1097.   throw new Error("G_GDEBUG constant must be set before loading debug.js");
  1098. }
  1099.  
  1100.  
  1101. /**
  1102.  * Write out a debugging message.
  1103.  *
  1104.  * @param who The thingy to convert into a zone name corresponding to the 
  1105.  *            zone to which this message belongs
  1106.  * @param msg Message to output
  1107.  */
  1108. function G_Debug(who, msg) {
  1109.   if (G_GDEBUG) {
  1110.     G_GetDebugZone(who).debug(msg);
  1111.   }
  1112. }
  1113.  
  1114. /**
  1115.  * Debugs loudly
  1116.  */
  1117. function G_DebugL(who, msg) {
  1118.   if (G_GDEBUG) {
  1119.     var zone = G_GetDebugZone(who);
  1120.  
  1121.     if (zone.zoneIsEnabled()) {
  1122.       G_debugService.dump(
  1123.         G_File.LINE_END_CHAR +
  1124.         "************************************************************" +
  1125.         G_File.LINE_END_CHAR);
  1126.  
  1127.       G_Debug(who, msg);
  1128.  
  1129.       G_debugService.dump(
  1130.         "************************************************************" +
  1131.         G_File.LINE_END_CHAR +
  1132.         G_File.LINE_END_CHAR);
  1133.     }
  1134.   }
  1135. }
  1136.  
  1137. /**
  1138.  * Write out a call tracing message
  1139.  *
  1140.  * @param who The thingy to convert into a zone name corresponding to the 
  1141.  *            zone to which this message belongs
  1142.  * @param msg Message to output
  1143.  */
  1144. function G_TraceCall(who, msg) {
  1145.   if (G_GDEBUG) {
  1146.     if (G_debugService.callTracingEnabled()) {
  1147.       G_debugService.dump(msg + G_File.LINE_END_CHAR);
  1148.     }
  1149.   }
  1150. }
  1151.  
  1152. /**
  1153.  * Write out an error (and throw)
  1154.  *
  1155.  * @param who The thingy to convert into a zone name corresponding to the 
  1156.  *            zone to which this message belongs
  1157.  * @param msg Message to output
  1158.  */
  1159. function G_Error(who, msg) {
  1160.   if (G_GDEBUG) {
  1161.     G_GetDebugZone(who).error(msg);
  1162.   }
  1163. }
  1164.  
  1165. /**
  1166.  * Assert something as true and signal an error if it's not
  1167.  *
  1168.  * @param who The thingy to convert into a zone name corresponding to the 
  1169.  *            zone to which this message belongs
  1170.  * @param condition Boolean condition to test
  1171.  * @param msg Message to output
  1172.  */
  1173. function G_Assert(who, condition, msg) {
  1174.   if (G_GDEBUG) {
  1175.     G_GetDebugZone(who).assert(condition, msg);
  1176.   }
  1177. }
  1178.  
  1179. /**
  1180.  * Assert two things are equal (as in ==).
  1181.  */
  1182. function G_AssertEqual(who, expected, actual, msg) {
  1183.   if (G_GDEBUG) {
  1184.     G_GetDebugZone(who).assert(
  1185.         expected == actual,
  1186.         msg + " Expected: {%s}, got: {%s}".subs(expected, actual));
  1187.   }
  1188. }
  1189.  
  1190. /**
  1191.  * Helper function that takes input and returns the DebugZone
  1192.  * corresponding to it.
  1193.  *
  1194.  * @param who Arbitrary input that will be converted into a zone name. Most
  1195.  *            likely an object that has .debugZone property, or a string.
  1196.  * @returns The DebugZone object corresponding to the input
  1197.  */
  1198. function G_GetDebugZone(who) {
  1199.   if (G_GDEBUG) {
  1200.     var zone = "?";
  1201.  
  1202.     if (who && who.debugZone) {
  1203.       zone = who.debugZone;
  1204.     } else if (isString(who)) {
  1205.       zone = who;
  1206.     }
  1207.  
  1208.     return G_debugService.getZone(zone);
  1209.   }
  1210. }
  1211.  
  1212. // Classes that implement the functionality.
  1213.  
  1214. /**
  1215.  * A debug "zone" is a string derived from arbitrary types (but
  1216.  * typically derived from another string or an object). All debugging
  1217.  * messages using a particular zone can be enabled or disabled
  1218.  * independent of other zones. This enables you to turn on/off logging
  1219.  * of particular objects or modules. This object implements a single
  1220.  * zone and the methods required to use it.
  1221.  *
  1222.  * @constructor
  1223.  * @param service Reference to the DebugService object we use for 
  1224.  *                registration
  1225.  * @param prefix String indicating the unique prefix we should use
  1226.  *               when creating preferences to control this zone
  1227.  * @param zone String indicating the name of the zone
  1228.  */
  1229. function G_DebugZone(service, prefix, zone) {
  1230.   if (G_GDEBUG) {
  1231.     this.debugService_ = service;
  1232.     this.prefix_ = prefix;
  1233.     this.zone_ = zone;
  1234.     this.zoneEnabledPrefName_ = prefix + ".zone." + this.zone_;
  1235.     this.settings_ = new G_DebugSettings();
  1236.   }
  1237. }
  1238.   
  1239. /**
  1240.  * @returns Boolean indicating if this zone is enabled
  1241.  */
  1242. G_DebugZone.prototype.zoneIsEnabled = function() {
  1243.   if (G_GDEBUG) {
  1244.     var explicit = this.settings_.getSetting(this.zoneEnabledPrefName_, null);
  1245.  
  1246.     if (explicit !== null) {
  1247.       return explicit;
  1248.     } else {
  1249.       return this.debugService_.allZonesEnabled();
  1250.     }
  1251.   }
  1252. }
  1253.  
  1254. /**
  1255.  * Enable this logging zone
  1256.  */
  1257. G_DebugZone.prototype.enableZone = function() {
  1258.   if (G_GDEBUG) {
  1259.     this.settings_.setDefault(this.zoneEnabledPrefName_, true);
  1260.   }
  1261. }
  1262.  
  1263. /**
  1264.  * Disable this logging zone
  1265.  */
  1266. G_DebugZone.prototype.disableZone = function() {
  1267.   if (G_GDEBUG) {
  1268.     this.settings_.setDefault(this.zoneEnabledPrefName_, false);
  1269.   }
  1270. }
  1271.  
  1272. /**
  1273.  * Write a debugging message to this zone
  1274.  *
  1275.  * @param msg String of message to write
  1276.  */
  1277. G_DebugZone.prototype.debug = function(msg) {
  1278.   if (G_GDEBUG) {
  1279.     if (this.zoneIsEnabled()) {
  1280.       this.debugService_.dump("[%s] %s%s".subs(this.zone_,
  1281.                                                msg,
  1282.                                                G_File.LINE_END_CHAR));
  1283.     }
  1284.   }
  1285. }
  1286.  
  1287. /**
  1288.  * Write an error to this zone and throw
  1289.  *
  1290.  * @param msg String of error to write
  1291.  */
  1292. G_DebugZone.prototype.error = function(msg) {
  1293.   if (G_GDEBUG) {
  1294.     this.debugService_.dump("[%s] %s%s".subs(this.zone_,
  1295.                                              msg,
  1296.                                              G_File.LINE_END_CHAR));
  1297.     throw new Error(msg);
  1298.     debugger;
  1299.   }
  1300. }
  1301.  
  1302. /**
  1303.  * Assert something as true and error if it is not
  1304.  *
  1305.  * @param condition Boolean condition to test
  1306.  * @param msg String of message to write if is false
  1307.  */
  1308. G_DebugZone.prototype.assert = function(condition, msg) {
  1309.   if (G_GDEBUG) {
  1310.     if (condition !== true) {
  1311.       G_Error(this.zone_, "ASSERT FAILED: " + msg);
  1312.     }
  1313.   }
  1314. }
  1315.  
  1316.  
  1317. /**
  1318.  * The debug service handles auto-registration of zones, namespacing
  1319.  * the zones preferences, and various global settings such as whether
  1320.  * all zones are enabled.
  1321.  *
  1322.  * @constructor
  1323.  * @param opt_prefix Optional string indicating the unique prefix we should 
  1324.  *                   use when creating preferences
  1325.  */
  1326. function G_DebugService(opt_prefix) {
  1327.   if (G_GDEBUG) {
  1328.     this.prefix_ = opt_prefix ? opt_prefix : "safebrowsing-debug-service";
  1329.     this.consoleEnabledPrefName_ = this.prefix_ + ".alsologtoconsole";
  1330.     this.allZonesEnabledPrefName_ = this.prefix_ + ".enableallzones";
  1331.     this.callTracingEnabledPrefName_ = this.prefix_ + ".trace-function-calls";
  1332.     this.logFileEnabledPrefName_ = this.prefix_ + ".logfileenabled";
  1333.     this.logFileErrorLevelPrefName_ = this.prefix_ + ".logfile-errorlevel";
  1334.     this.zones_ = {};
  1335.  
  1336.     this.loggifier = new G_Loggifier();
  1337.     this.settings_ = new G_DebugSettings();
  1338.  
  1339.     // We observe the console service so that we can echo errors that get 
  1340.     // reported there to the file log.
  1341.     Cc["@mozilla.org/consoleservice;1"]
  1342.       .getService(Ci.nsIConsoleService)
  1343.       .registerListener(this);
  1344.   }
  1345. }
  1346.  
  1347. // Error levels for reporting console messages to the log.
  1348. G_DebugService.ERROR_LEVEL_INFO = "INFO";
  1349. G_DebugService.ERROR_LEVEL_WARNING = "WARNING";
  1350. G_DebugService.ERROR_LEVEL_EXCEPTION = "EXCEPTION";
  1351.  
  1352.  
  1353. /**
  1354.  * @returns Boolean indicating if we should send messages to the jsconsole
  1355.  */
  1356. G_DebugService.prototype.alsoDumpToConsole = function() {
  1357.   if (G_GDEBUG) {
  1358.     return this.settings_.getSetting(this.consoleEnabledPrefName_, false);
  1359.   }
  1360. }
  1361.  
  1362. /**
  1363.  * @returns whether to log output to a file as well as the console.
  1364.  */
  1365. G_DebugService.prototype.logFileIsEnabled = function() {
  1366.   if (G_GDEBUG) {
  1367.     return this.settings_.getSetting(this.logFileEnabledPrefName_, false);
  1368.   }
  1369. }
  1370.  
  1371. /**
  1372.  * Turns on file logging. dump() output will also go to the file specified by
  1373.  * setLogFile()
  1374.  */
  1375. G_DebugService.prototype.enableLogFile = function() {
  1376.   if (G_GDEBUG) {
  1377.     this.settings_.setDefault(this.logFileEnabledPrefName_, true);
  1378.   }
  1379. }
  1380.  
  1381. /**
  1382.  * Turns off file logging
  1383.  */
  1384. G_DebugService.prototype.disableLogFile = function() {
  1385.   if (G_GDEBUG) {
  1386.     this.settings_.setDefault(this.logFileEnabledPrefName_, false);
  1387.   }
  1388. }
  1389.  
  1390. /**
  1391.  * @returns an nsIFile instance pointing to the current log file location
  1392.  */
  1393. G_DebugService.prototype.getLogFile = function() {
  1394.   if (G_GDEBUG) {
  1395.     return this.logFile_;
  1396.   }
  1397. }
  1398.  
  1399. /**
  1400.  * Sets a new log file location
  1401.  */
  1402. G_DebugService.prototype.setLogFile = function(file) {
  1403.   if (G_GDEBUG) {
  1404.     this.logFile_ = file;
  1405.   }
  1406. }
  1407.  
  1408. /**
  1409.  * Enables sending messages to the jsconsole
  1410.  */
  1411. G_DebugService.prototype.enableDumpToConsole = function() {
  1412.   if (G_GDEBUG) {
  1413.     this.settings_.setDefault(this.consoleEnabledPrefName_, true);
  1414.   }
  1415. }
  1416.  
  1417. /**
  1418.  * Disables sending messages to the jsconsole
  1419.  */
  1420. G_DebugService.prototype.disableDumpToConsole = function() {
  1421.   if (G_GDEBUG) {
  1422.     this.settings_.setDefault(this.consoleEnabledPrefName_, false);
  1423.   }
  1424. }
  1425.  
  1426. /**
  1427.  * @param zone Name of the zone to get
  1428.  * @returns The DebugZone object corresopnding to input. If not such
  1429.  *          zone exists, a new one is created and returned
  1430.  */
  1431. G_DebugService.prototype.getZone = function(zone) {
  1432.   if (G_GDEBUG) {
  1433.     if (!this.zones_[zone]) 
  1434.       this.zones_[zone] = new G_DebugZone(this, this.prefix_, zone);
  1435.     
  1436.     return this.zones_[zone];
  1437.   }
  1438. }
  1439.  
  1440. /**
  1441.  * @param zone Zone to enable debugging for
  1442.  */
  1443. G_DebugService.prototype.enableZone = function(zone) {
  1444.   if (G_GDEBUG) {
  1445.     var toEnable = this.getZone(zone);
  1446.     toEnable.enableZone();
  1447.   }
  1448. }
  1449.  
  1450. /**
  1451.  * @param zone Zone to disable debugging for
  1452.  */
  1453. G_DebugService.prototype.disableZone = function(zone) {
  1454.   if (G_GDEBUG) {
  1455.     var toDisable = this.getZone(zone);
  1456.     toDisable.disableZone();
  1457.   }
  1458. }
  1459.  
  1460. /**
  1461.  * @returns Boolean indicating whether debugging is enabled for all zones
  1462.  */
  1463. G_DebugService.prototype.allZonesEnabled = function() {
  1464.   if (G_GDEBUG) {
  1465.     return this.settings_.getSetting(this.allZonesEnabledPrefName_, false);
  1466.   }
  1467. }
  1468.  
  1469. /**
  1470.  * Enables all debugging zones
  1471.  */
  1472. G_DebugService.prototype.enableAllZones = function() {
  1473.   if (G_GDEBUG) {
  1474.     this.settings_.setDefault(this.allZonesEnabledPrefName_, true);
  1475.   }
  1476. }
  1477.  
  1478. /**
  1479.  * Disables all debugging zones
  1480.  */
  1481. G_DebugService.prototype.disableAllZones = function() {
  1482.   if (G_GDEBUG) {
  1483.     this.settings_.setDefault(this.allZonesEnabledPrefName_, false);
  1484.   }
  1485. }
  1486.  
  1487. /**
  1488.  * @returns Boolean indicating whether call tracing is enabled
  1489.  */
  1490. G_DebugService.prototype.callTracingEnabled = function() {
  1491.   if (G_GDEBUG) {
  1492.     return this.settings_.getSetting(this.callTracingEnabledPrefName_, false);
  1493.   }
  1494. }
  1495.  
  1496. /**
  1497.  * Enables call tracing
  1498.  */
  1499. G_DebugService.prototype.enableCallTracing = function() {
  1500.   if (G_GDEBUG) {
  1501.     this.settings_.setDefault(this.callTracingEnabledPrefName_, true);
  1502.   }
  1503. }
  1504.  
  1505. /**
  1506.  * Disables call tracing
  1507.  */
  1508. G_DebugService.prototype.disableCallTracing = function() {
  1509.   if (G_GDEBUG) {
  1510.     this.settings_.setDefault(this.callTracingEnabledPrefName_, false);
  1511.   }
  1512. }
  1513.  
  1514. /**
  1515.  * Gets the minimum error that will be reported to the log.
  1516.  */
  1517. G_DebugService.prototype.getLogFileErrorLevel = function() {
  1518.   if (G_GDEBUG) {
  1519.     var level = this.settings_.getSetting(this.logFileErrorLevelPrefName_, 
  1520.                                           G_DebugService.ERROR_LEVEL_EXCEPTION);
  1521.  
  1522.     return level.toUpperCase();
  1523.   }
  1524. }
  1525.  
  1526. /**
  1527.  * Sets the minimum error level that will be reported to the log.
  1528.  */
  1529. G_DebugService.prototype.setLogFileErrorLevel = function(level) {
  1530.   if (G_GDEBUG) {
  1531.     // normalize case just to make it slightly easier to not screw up.
  1532.     level = level.toUpperCase();
  1533.  
  1534.     if (level != G_DebugService.ERROR_LEVEL_INFO &&
  1535.         level != G_DebugService.ERROR_LEVEL_WARNING &&
  1536.         level != G_DebugService.ERROR_LEVEL_EXCEPTION) {
  1537.       throw new Error("Invalid error level specified: {" + level + "}");
  1538.     }
  1539.  
  1540.     this.settings_.setDefault(this.logFileErrorLevelPrefName_, level);
  1541.   }
  1542. }
  1543.  
  1544. /**
  1545.  * Internal dump() method
  1546.  *
  1547.  * @param msg String of message to dump
  1548.  */
  1549. G_DebugService.prototype.dump = function(msg) {
  1550.   if (G_GDEBUG) {
  1551.     dump(msg);
  1552.     
  1553.     if (this.alsoDumpToConsole()) {
  1554.       try {
  1555.         var console = Components.classes['@mozilla.org/consoleservice;1']
  1556.                       .getService(Components.interfaces.nsIConsoleService);
  1557.         console.logStringMessage(msg);
  1558.       } catch(e) {
  1559.         dump("G_DebugZone ERROR: COULD NOT DUMP TO CONSOLE" +
  1560.              G_File.LINE_END_CHAR);
  1561.       }
  1562.     }
  1563.  
  1564.     this.maybeDumpToFile(msg);
  1565.   }
  1566. }
  1567.  
  1568. /**
  1569.  * Writes the specified message to the log file, if file logging is enabled.
  1570.  */
  1571. G_DebugService.prototype.maybeDumpToFile = function(msg) {
  1572.   if (this.logFileIsEnabled() && this.logFile_) {
  1573.     if (!this.logWriter_) {
  1574.       this.logWriter_ = new G_FileWriter(this.logFile_, true);
  1575.     }
  1576.  
  1577.     this.logWriter_.write(msg);
  1578.   }
  1579. }
  1580.  
  1581. /**
  1582.  * Implements nsIConsoleListener.observe(). Gets called when an error message
  1583.  * gets reported to the console and sends it to the log file as well.
  1584.  */
  1585. G_DebugService.prototype.observe = function(consoleMessage) {
  1586.   if (G_GDEBUG) {
  1587.     var errorLevel = this.getLogFileErrorLevel();
  1588.  
  1589.     // consoleMessage can be either nsIScriptError or nsIConsoleMessage. The
  1590.     // latter does not have things like line number, etc. So we special case 
  1591.     // it first. 
  1592.     if (!(consoleMessage instanceof Ci.nsIScriptError)) {
  1593.       // Only report these messages if the error level is INFO.
  1594.       if (errorLevel == G_DebugService.ERROR_LEVEL_INFO) {
  1595.         this.maybeDumpToFile(G_DebugService.ERROR_LEVEL_INFO + ": " + 
  1596.                              consoleMessage.message + G_File.LINE_END_CHAR);
  1597.       }
  1598.  
  1599.       return;
  1600.     }
  1601.  
  1602.     // We make a local copy of these fields because writing to it doesn't seem
  1603.     // to work.
  1604.     var flags = consoleMessage.flags;
  1605.     var sourceName = consoleMessage.sourceName;
  1606.     var lineNumber = consoleMessage.lineNumber;
  1607.  
  1608.     // Sometimes, a scripterror instance won't have any flags set. We 
  1609.     // default to exception.
  1610.     if (!flags) {
  1611.       flags = Ci.nsIScriptError.exceptionFlag;
  1612.     }
  1613.  
  1614.     // Default the filename and line number if they aren't set.
  1615.     if (!sourceName) {
  1616.       sourceName = "<unknown>";
  1617.     }
  1618.  
  1619.     if (!lineNumber) {
  1620.       lineNumber = "<unknown>";
  1621.     }
  1622.  
  1623.     // Report the error in the log file.
  1624.     if (flags & Ci.nsIScriptError.warningFlag) {
  1625.       // Only report warnings if the error level is warning or better. 
  1626.       if (errorLevel == G_DebugService.ERROR_LEVEL_WARNING ||
  1627.           errorLevel == G_DebugService.ERROR_LEVEL_INFO) {
  1628.         this.reportScriptError_(consoleMessage.message,
  1629.                                 sourceName,
  1630.                                 lineNumber,
  1631.                                 G_DebugService.ERROR_LEVEL_WARNING);
  1632.       }
  1633.     } else if (flags & Ci.nsIScriptError.exceptionFlag) {
  1634.       // Always report exceptions.
  1635.       this.reportScriptError_(consoleMessage.message,
  1636.                               sourceName,
  1637.                               lineNumber,
  1638.                               G_DebugService.ERROR_LEVEL_EXCEPTION);
  1639.     }
  1640.   }
  1641. }
  1642.  
  1643. /**
  1644.  * Private helper to report an nsIScriptError instance to the log/console.
  1645.  */
  1646. G_DebugService.prototype.reportScriptError_ = function(message, sourceName, 
  1647.                                                        lineNumber, label) {
  1648.   var message = ["",
  1649.                  "------------------------------------------------------------",
  1650.                  label + ": " + message,
  1651.                  "location: " + sourceName + ", " + "line: " + lineNumber,
  1652.                  "------------------------------------------------------------",
  1653.                  "",
  1654.                  ""].join(G_File.LINE_END_CHAR);
  1655.  
  1656.   dump(message);
  1657.   this.maybeDumpToFile(message);
  1658. }
  1659.  
  1660.  
  1661.  
  1662. /**
  1663.  * A class that instruments methods so they output a call trace,
  1664.  * including the values of their actual parameters and return value.
  1665.  * This code is mostly stolen from Aaron Boodman's original
  1666.  * implementation in clobber utils.
  1667.  *
  1668.  * Note that this class uses the "loggifier" debug zone, so you'll see 
  1669.  * a complete call trace when that zone is enabled.
  1670.  *
  1671.  * @constructor
  1672.  */
  1673. function G_Loggifier() {
  1674.   if (G_GDEBUG) {
  1675.     // Careful not to loggify ourselves!
  1676.     this.mark_(this);  
  1677.   }
  1678. }
  1679.  
  1680. /**
  1681.  * Marks an object as having been loggified. Loggification is not 
  1682.  * idempotent :)
  1683.  *
  1684.  * @param obj Object to be marked
  1685.  */
  1686. G_Loggifier.prototype.mark_ = function(obj) {
  1687.   if (G_GDEBUG) {
  1688.     obj.__loggified_ = true;
  1689.   }
  1690. }
  1691.  
  1692. /**
  1693.  * @param obj Object to be examined
  1694.  * @returns Boolean indicating if the object has been loggified
  1695.  */
  1696. G_Loggifier.prototype.isLoggified = function(obj) {
  1697.   if (G_GDEBUG) {
  1698.     return !!obj.__loggified_;
  1699.   }
  1700. }
  1701.  
  1702. /**
  1703.  * Attempt to extract the class name from the constructor definition.
  1704.  * Assumes the object was created using new.
  1705.  *
  1706.  * @param constructor String containing the definition of a constructor,
  1707.  *                    for example what you'd get by examining obj.constructor
  1708.  * @returns Name of the constructor/object if it could be found, else "???"
  1709.  */
  1710. G_Loggifier.prototype.getFunctionName_ = function(constructor) {
  1711.   if (G_GDEBUG) {
  1712.     return constructor.name || "???";
  1713.   }
  1714. }
  1715.  
  1716. /**
  1717.  * Wraps all the methods in an object so that call traces are
  1718.  * automatically outputted.
  1719.  *
  1720.  * @param obj Object to loggify. SHOULD BE THE PROTOTYPE OF A USER-DEFINED
  1721.  *            object. You can get into trouble if you attempt to 
  1722.  *            loggify something that isn't, for example the Window.
  1723.  *
  1724.  * Any additional parameters are considered method names which should not be
  1725.  * loggified.
  1726.  *
  1727.  * Usage:
  1728.  * G_debugService.loggifier.loggify(MyClass.prototype,
  1729.  *                                  "firstMethodNotToLog",
  1730.  *                                  "secondMethodNotToLog",
  1731.  *                                  ... etc ...);
  1732.  */
  1733. G_Loggifier.prototype.loggify = function(obj) {
  1734.   if (G_GDEBUG) {
  1735.     if (!G_debugService.callTracingEnabled()) {
  1736.       return;
  1737.     }
  1738.  
  1739.     if (typeof window != "undefined" && obj == window || 
  1740.         this.isLoggified(obj))   // Don't go berserk!
  1741.       return;
  1742.  
  1743.     var zone = G_GetDebugZone(obj);
  1744.     if (!zone || !zone.zoneIsEnabled()) {
  1745.       return;
  1746.     }
  1747.  
  1748.     this.mark_(obj);
  1749.  
  1750.     // Helper function returns an instrumented version of
  1751.     // objName.meth, with "this" bound properly. (BTW, because we're
  1752.     // in a conditional here, functions will only be defined as
  1753.     // they're encountered during execution, so declare this helper
  1754.     // before using it.)
  1755.  
  1756.     function wrap(meth, objName, methName) {
  1757.       return function() {
  1758.         
  1759.         // First output the call along with actual parameters
  1760.         var args = new Array(arguments.length);
  1761.         var argsString = "";
  1762.         for (var i = 0; i < args.length; i++) {
  1763.           args[i] = arguments[i];
  1764.           argsString += (i == 0 ? "" : ", ");
  1765.           
  1766.           if (isFunction(args[i])) {
  1767.             argsString += "[function]";
  1768.           } else {
  1769.             argsString += args[i];
  1770.           }
  1771.         }
  1772.  
  1773.         G_TraceCall(this, "> " + objName + "." + methName + "(" + 
  1774.                     argsString + ")");
  1775.         
  1776.         // Then run the function, capturing the return value and throws
  1777.         try {
  1778.           var retVal = meth.apply(this, arguments);
  1779.           var reportedRetVal = retVal;
  1780.  
  1781.           if (typeof reportedRetVal == "undefined")
  1782.             reportedRetVal = "void";
  1783.           else if (reportedRetVal === "")
  1784.             reportedRetVal = "\"\" (empty string)";
  1785.         } catch (e) {
  1786.           if (e && !e.__logged) {
  1787.             G_TraceCall(this, "Error: " + e.message + ". " + 
  1788.                         e.fileName + ": " + e.lineNumber);
  1789.             try {
  1790.               e.__logged = true;
  1791.             } catch (e2) {
  1792.               // Sometimes we can't add the __logged flag because it's an
  1793.               // XPC wrapper
  1794.               throw e;
  1795.             }
  1796.           }
  1797.           
  1798.           throw e;      // Re-throw!
  1799.         }
  1800.  
  1801.         // And spit it out already
  1802.         G_TraceCall(
  1803.           this, 
  1804.           "< " + objName + "." + methName + ": " + reportedRetVal);
  1805.  
  1806.         return retVal;
  1807.       };
  1808.     };
  1809.  
  1810.     var ignoreLookup = {};
  1811.  
  1812.     if (arguments.length > 1) {
  1813.       for (var i = 1; i < arguments.length; i++) {
  1814.         ignoreLookup[arguments[i]] = true;
  1815.       }
  1816.     }
  1817.     
  1818.     // Wrap each method of obj
  1819.     for (var p in obj) {
  1820.       // Work around bug in Firefox. In ffox typeof RegExp is "function",
  1821.       // so make sure this really is a function. Bug as of FFox 1.5b2.
  1822.       if (typeof obj[p] == "function" && obj[p].call && !ignoreLookup[p]) {
  1823.         var objName = this.getFunctionName_(obj.constructor);
  1824.         obj[p] = wrap(obj[p], objName, p);
  1825.       }
  1826.     }
  1827.   }
  1828. }
  1829.  
  1830.  
  1831. /**
  1832.  * Simple abstraction around debug settings. The thing with debug settings is
  1833.  * that we want to be able to specify a default in the application's startup,
  1834.  * but have that default be overridable by the user via their prefs.
  1835.  *
  1836.  * To generalize this, we package up a dictionary of defaults with the 
  1837.  * preferences tree. If a setting isn't in the preferences tree, then we grab it
  1838.  * from the defaults.
  1839.  */
  1840. function G_DebugSettings() {
  1841.   this.defaults_ = {};
  1842.   this.prefs_ = new G_Preferences();
  1843. }
  1844.  
  1845. /**
  1846.  * Returns the value of a settings, optionally defaulting to a given value if it
  1847.  * doesn't exist. If no default is specified, the default is |undefined|.
  1848.  */
  1849. G_DebugSettings.prototype.getSetting = function(name, opt_default) {
  1850.   var override = this.prefs_.getPref(name, null);
  1851.  
  1852.   if (override !== null) {
  1853.     return override;
  1854.   } else if (typeof this.defaults_[name] != "undefined") {
  1855.     return this.defaults_[name];
  1856.   } else {
  1857.     return opt_default;
  1858.   }
  1859. }
  1860.  
  1861. /**
  1862.  * Sets the default value for a setting. If the user doesn't override it with a
  1863.  * preference, this is the value which will be returned by getSetting().
  1864.  */
  1865. G_DebugSettings.prototype.setDefault = function(name, val) {
  1866.   this.defaults_[name] = val;
  1867. }
  1868.  
  1869. var G_debugService = new G_DebugService(); // Instantiate us!
  1870.  
  1871. if (G_GDEBUG) {
  1872.   G_debugService.enableAllZones();
  1873. }
  1874. /* ***** BEGIN LICENSE BLOCK *****
  1875.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  1876.  *
  1877.  * The contents of this file are subject to the Mozilla Public License Version
  1878.  * 1.1 (the "License"); you may not use this file except in compliance with
  1879.  * the License. You may obtain a copy of the License at
  1880.  * http://www.mozilla.org/MPL/
  1881.  *
  1882.  * Software distributed under the License is distributed on an "AS IS" basis,
  1883.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  1884.  * for the specific language governing rights and limitations under the
  1885.  * License.
  1886.  *
  1887.  * The Original Code is Google Safe Browsing.
  1888.  *
  1889.  * The Initial Developer of the Original Code is Google Inc.
  1890.  * Portions created by the Initial Developer are Copyright (C) 2006
  1891.  * the Initial Developer. All Rights Reserved.
  1892.  *
  1893.  * Contributor(s):
  1894.  *   Fritz Schneider <fritz@google.com> (original author)
  1895.  *
  1896.  * Alternatively, the contents of this file may be used under the terms of
  1897.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  1898.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1899.  * in which case the provisions of the GPL or the LGPL are applicable instead
  1900.  * of those above. If you wish to allow use of your version of this file only
  1901.  * under the terms of either the GPL or the LGPL, and not to allow others to
  1902.  * use your version of this file under the terms of the MPL, indicate your
  1903.  * decision by deleting the provisions above and replace them with the notice
  1904.  * and other provisions required by the GPL or the LGPL. If you do not delete
  1905.  * the provisions above, a recipient may use your version of this file under
  1906.  * the terms of any one of the MPL, the GPL or the LGPL.
  1907.  *
  1908.  * ***** END LICENSE BLOCK ***** */
  1909.  
  1910.  
  1911. // An Alarm fires a callback after a certain amount of time, or at
  1912. // regular intervals. It's a convenient replacement for
  1913. // setTimeout/Interval when you don't want to bind to a specific
  1914. // window.
  1915. //
  1916. // The ConditionalAlarm is an Alarm that cancels itself if its callback 
  1917. // returns a value that type-converts to true.
  1918. //
  1919. // Example:
  1920. //
  1921. //  function foo() { alert('hi'); };
  1922. //  new G_Alarm(foo, 10*1000);                   // Fire foo in 10 seconds
  1923. //  new G_Alarm(foo, 10*1000, true /*repeat*/);  // Fire foo every 10 seconds
  1924. //  new G_Alarm(foo, 10*1000, true, 7);          // Fire foo every 10 seconds
  1925. //                                               // seven times
  1926. //  new G_ConditionalAlarm(foo, 1000, true); // Fire every sec until foo()==true
  1927. //
  1928. //  // Fire foo every 10 seconds until foo returns true or until it fires seven
  1929. //  // times, whichever happens first.
  1930. //  new G_ConditionalAlarm(foo, 10*1000, true /*repeating*/, 7);
  1931. //
  1932. // TODO: maybe pass an isFinal flag to the callback if they opted to
  1933. // set maxTimes and this is the last iteration?
  1934.  
  1935.  
  1936. /**
  1937.  * Set an alarm to fire after a given amount of time, or at specific 
  1938.  * intervals.
  1939.  *
  1940.  * @param callback Function to call when the alarm fires
  1941.  * @param delayMS Number indicating the length of the alarm period in ms
  1942.  * @param opt_repeating Boolean indicating whether this should fire 
  1943.  *                      periodically
  1944.  * @param opt_maxTimes Number indicating a maximum number of times to 
  1945.  *                     repeat (obviously only useful when opt_repeating==true)
  1946.  */
  1947. function G_Alarm(callback, delayMS, opt_repeating, opt_maxTimes) {
  1948.   this.debugZone = "alarm";
  1949.   this.callback_ = callback;
  1950.   this.repeating_ = !!opt_repeating;
  1951.   this.timer_ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  1952.   var type = opt_repeating ? 
  1953.              this.timer_.TYPE_REPEATING_SLACK : 
  1954.              this.timer_.TYPE_ONE_SHOT;
  1955.   this.maxTimes_ = opt_maxTimes ? opt_maxTimes : null;
  1956.   this.nTimes_ = 0;
  1957.  
  1958.   this.observerServiceObserver_ = new G_ObserverServiceObserver(
  1959.                                         'xpcom-shutdown',
  1960.                                         BindToObject(this.cancel, this));
  1961.  
  1962.   // Ask the timer to use nsITimerCallback (.notify()) when ready
  1963.   this.timer_.initWithCallback(this, delayMS, type);
  1964. }
  1965.  
  1966. /**
  1967.  * Cancel this timer 
  1968.  */
  1969. G_Alarm.prototype.cancel = function() {
  1970.   if (!this.timer_) {
  1971.     return;
  1972.   }
  1973.  
  1974.   this.timer_.cancel();
  1975.   // Break circular reference created between this.timer_ and the G_Alarm
  1976.   // instance (this)
  1977.   this.timer_ = null;
  1978.   this.callback_ = null;
  1979.  
  1980.   // We don't need the shutdown observer anymore
  1981.   this.observerServiceObserver_.unregister();
  1982. }
  1983.  
  1984. /**
  1985.  * Invoked by the timer when it fires
  1986.  * 
  1987.  * @param timer Reference to the nsITimer which fired (not currently 
  1988.  *              passed along)
  1989.  */
  1990. G_Alarm.prototype.notify = function(timer) {
  1991.   // fire callback and save results
  1992.   var ret = this.callback_();
  1993.   
  1994.   // If they've given us a max number of times to fire, enforce it
  1995.   this.nTimes_++;
  1996.   if (this.repeating_ && 
  1997.       typeof this.maxTimes_ == "number" 
  1998.       && this.nTimes_ >= this.maxTimes_) {
  1999.     this.cancel();
  2000.   } else if (!this.repeating_) {
  2001.     // Clear out the callback closure for TYPE_ONE_SHOT timers
  2002.     this.cancel();
  2003.   }
  2004.   // We don't cancel/cleanup timers that repeat forever until either
  2005.   // xpcom-shutdown occurs or cancel() is called explicitly.
  2006.  
  2007.   return ret;
  2008. }
  2009.  
  2010. /**
  2011.  * XPCOM cruft
  2012.  */
  2013. G_Alarm.prototype.QueryInterface = function(iid) {
  2014.   if (iid.equals(Components.interfaces.nsISupports) ||
  2015.       iid.equals(Components.interfaces.nsITimerCallback))
  2016.     return this;
  2017.  
  2018.   throw Components.results.NS_ERROR_NO_INTERFACE;
  2019. }
  2020.  
  2021.  
  2022. /**
  2023.  * An alarm with the additional property that it cancels itself if its 
  2024.  * callback returns true.
  2025.  *
  2026.  * For parameter documentation, see G_Alarm
  2027.  */
  2028. function G_ConditionalAlarm(callback, delayMS, opt_repeating, opt_maxTimes) {
  2029.   G_Alarm.call(this, callback, delayMS, opt_repeating, opt_maxTimes);
  2030.   this.debugZone = "conditionalalarm";
  2031. }
  2032.  
  2033. G_ConditionalAlarm.inherits(G_Alarm);
  2034.  
  2035. /**
  2036.  * Invoked by the timer when it fires
  2037.  * 
  2038.  * @param timer Reference to the nsITimer which fired (not currently 
  2039.  *              passed along)
  2040.  */
  2041. G_ConditionalAlarm.prototype.notify = function(timer) {
  2042.   // Call G_Alarm::notify
  2043.   var rv = G_Alarm.prototype.notify.call(this, timer);
  2044.  
  2045.   if (this.repeating_ && rv) {
  2046.     G_Debug(this, "Callback of a repeating alarm returned true; cancelling.");
  2047.     this.cancel();
  2048.   }
  2049. }
  2050. /* ***** BEGIN LICENSE BLOCK *****
  2051.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2052.  *
  2053.  * The contents of this file are subject to the Mozilla Public License Version
  2054.  * 1.1 (the "License"); you may not use this file except in compliance with
  2055.  * the License. You may obtain a copy of the License at
  2056.  * http://www.mozilla.org/MPL/
  2057.  *
  2058.  * Software distributed under the License is distributed on an "AS IS" basis,
  2059.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2060.  * for the specific language governing rights and limitations under the
  2061.  * License.
  2062.  *
  2063.  * The Original Code is Google Safe Browsing.
  2064.  *
  2065.  * The Initial Developer of the Original Code is Google Inc.
  2066.  * Portions created by the Initial Developer are Copyright (C) 2006
  2067.  * the Initial Developer. All Rights Reserved.
  2068.  *
  2069.  * Contributor(s):
  2070.  *   Fritz Schneider <fritz@google.com> (original author)
  2071.  *
  2072.  * Alternatively, the contents of this file may be used under the terms of
  2073.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2074.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2075.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2076.  * of those above. If you wish to allow use of your version of this file only
  2077.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2078.  * use your version of this file under the terms of the MPL, indicate your
  2079.  * decision by deleting the provisions above and replace them with the notice
  2080.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2081.  * the provisions above, a recipient may use your version of this file under
  2082.  * the terms of any one of the MPL, the GPL or the LGPL.
  2083.  *
  2084.  * ***** END LICENSE BLOCK ***** */
  2085.  
  2086.  
  2087. // Base64 en/decoding. Not much to say here except that we work with
  2088. // decoded values in arrays of bytes. By "byte" I mean a number in [0,
  2089. // 255].
  2090.  
  2091.  
  2092. /**
  2093.  * Base64 en/decoder. Useful in contexts that don't have atob/btoa, or
  2094.  * when you need a custom encoding function (e.g., websafe base64).
  2095.  *
  2096.  * @constructor
  2097.  */
  2098. function G_Base64() {
  2099.   this.byteToCharMap_ = {};
  2100.   this.charToByteMap_ = {};
  2101.   this.byteToCharMapWebSafe_ = {};
  2102.   this.charToByteMapWebSafe_ = {};
  2103.   this.init_();
  2104. }
  2105.  
  2106. /**
  2107.  * Our default alphabet. Value 64 (=) is special; it means "nothing."
  2108.  */ 
  2109. G_Base64.ENCODED_VALS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
  2110.                         "abcdefghijklmnopqrstuvwxyz" +
  2111.                         "0123456789+/=";
  2112.  
  2113. /**
  2114.  * Our websafe alphabet. Value 64 (=) is special; it means "nothing."
  2115.  */ 
  2116. G_Base64.ENCODED_VALS_WEBSAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
  2117.                                 "abcdefghijklmnopqrstuvwxyz" +
  2118.                                 "0123456789-_=";
  2119.  
  2120. /**
  2121.  * We want quick mappings back and forth, so we precompute two maps.
  2122.  */
  2123. G_Base64.prototype.init_ = function() {
  2124.   for (var i = 0; i < G_Base64.ENCODED_VALS.length; i++) {
  2125.     this.byteToCharMap_[i] = G_Base64.ENCODED_VALS.charAt(i);
  2126.     this.charToByteMap_[this.byteToCharMap_[i]] = i;
  2127.     this.byteToCharMapWebSafe_[i] = G_Base64.ENCODED_VALS_WEBSAFE.charAt(i);
  2128.     this.charToByteMapWebSafe_[this.byteToCharMapWebSafe_[i]] = i;
  2129.   }
  2130. }
  2131.  
  2132. /**
  2133.  * Base64-encode an array of bytes.
  2134.  *
  2135.  * @param input An array of bytes (numbers with value in [0, 255]) to encode
  2136.  *
  2137.  * @param opt_webSafe Boolean indicating we should use the alternative alphabet 
  2138.  *
  2139.  * @returns String containing the base64 encoding
  2140.  */
  2141. G_Base64.prototype.encodeByteArray = function(input, opt_webSafe) {
  2142.  
  2143.   if (!(input instanceof Array))
  2144.     throw new Error("encodeByteArray takes an array as a parameter");
  2145.  
  2146.   var byteToCharMap = opt_webSafe ? 
  2147.                       this.byteToCharMapWebSafe_ :
  2148.                       this.byteToCharMap_;
  2149.  
  2150.   var output = [];
  2151.  
  2152.   var i = 0;
  2153.   while (i < input.length) {
  2154.  
  2155.     var byte1 = input[i];
  2156.     var haveByte2 = i + 1 < input.length;
  2157.     var byte2 = haveByte2 ? input[i + 1] : 0;
  2158.     var haveByte3 = i + 2 < input.length;
  2159.     var byte3 = haveByte3 ? input[i + 2] : 0;
  2160.  
  2161.     var outByte1 = byte1 >> 2;
  2162.     var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
  2163.     var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);
  2164.     var outByte4 = byte3 & 0x3F;
  2165.  
  2166.     if (!haveByte3) {
  2167.       outByte4 = 64;
  2168.       
  2169.       if (!haveByte2)
  2170.         outByte3 = 64;
  2171.     }
  2172.     
  2173.     output.push(byteToCharMap[outByte1]);
  2174.     output.push(byteToCharMap[outByte2]);
  2175.     output.push(byteToCharMap[outByte3]);
  2176.     output.push(byteToCharMap[outByte4]);
  2177.  
  2178.     i += 3;
  2179.   }
  2180.  
  2181.   return output.join("");
  2182. }
  2183.  
  2184. /**
  2185.  * Base64-decode a string.
  2186.  *
  2187.  * @param input String to decode
  2188.  *
  2189.  * @param opt_webSafe Boolean indicating we should use the alternative alphabet 
  2190.  * 
  2191.  * @returns Array of bytes representing the decoded value.
  2192.  */
  2193. G_Base64.prototype.decodeString = function(input, opt_webSafe) {
  2194.  
  2195.   if (input.length % 4)
  2196.     throw new Error("Length of b64-encoded data must be zero mod four");
  2197.  
  2198.   var charToByteMap = opt_webSafe ? 
  2199.                       this.charToByteMapWebSafe_ :
  2200.                       this.charToByteMap_;
  2201.  
  2202.   var output = [];
  2203.  
  2204.   var i = 0;
  2205.   while (i < input.length) {
  2206.  
  2207.     var byte1 = charToByteMap[input.charAt(i)];
  2208.     var byte2 = charToByteMap[input.charAt(i + 1)];
  2209.     var byte3 = charToByteMap[input.charAt(i + 2)];
  2210.     var byte4 = charToByteMap[input.charAt(i + 3)];
  2211.  
  2212.     if (byte1 === undefined || byte2 === undefined ||
  2213.         byte3 === undefined || byte4 === undefined)
  2214.       throw new Error("String contains characters not in our alphabet: " +
  2215.                       input);
  2216.  
  2217.     var outByte1 = (byte1 << 2) | (byte2 >> 4);
  2218.     output.push(outByte1);
  2219.     
  2220.     if (byte3 != 64) {
  2221.       var outByte2 = ((byte2 << 4) & 0xF0) | (byte3 >> 2);
  2222.       output.push(outByte2);
  2223.       
  2224.       if (byte4 != 64) {
  2225.         var outByte3 = ((byte3 << 6) & 0xC0) | byte4;
  2226.         output.push(outByte3);
  2227.       }
  2228.     }
  2229.  
  2230.     i += 4;
  2231.   }
  2232.  
  2233.   return output;
  2234. }
  2235.  
  2236. /**
  2237.  * Helper function that turns a string into an array of numbers. 
  2238.  *
  2239.  * @param str String to arrify
  2240.  *
  2241.  * @returns Array holding numbers corresponding to the UCS character codes
  2242.  *          of each character in str
  2243.  */
  2244. G_Base64.prototype.arrayifyString = function(str) {
  2245.   var output = [];
  2246.   for (var i = 0; i < str.length; i++)
  2247.     output.push(str.charCodeAt(i));
  2248.   return output;
  2249. }
  2250.  
  2251. /**
  2252.  * Helper function that turns an array of numbers into the string
  2253.  * given by the concatenation of the characters to which the numbesr
  2254.  * correspond (got that?).
  2255.  *
  2256.  * @param array Array of numbers representing characters
  2257.  *
  2258.  * @returns Stringification of the array
  2259.  */ 
  2260. G_Base64.prototype.stringifyArray = function(array) {
  2261.   var output = [];
  2262.   for (var i = 0; i < array.length; i++)
  2263.     output[i] = String.fromCharCode(array[i]);
  2264.   return output.join("");
  2265. }
  2266.  
  2267.  
  2268. /* ***** BEGIN LICENSE BLOCK *****
  2269.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2270.  *
  2271.  * The contents of this file are subject to the Mozilla Public License Version
  2272.  * 1.1 (the "License"); you may not use this file except in compliance with
  2273.  * the License. You may obtain a copy of the License at
  2274.  * http://www.mozilla.org/MPL/
  2275.  *
  2276.  * Software distributed under the License is distributed on an "AS IS" basis,
  2277.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2278.  * for the specific language governing rights and limitations under the
  2279.  * License.
  2280.  *
  2281.  * The Original Code is Google Safe Browsing.
  2282.  *
  2283.  * The Initial Developer of the Original Code is Google Inc.
  2284.  * Portions created by the Initial Developer are Copyright (C) 2006
  2285.  * the Initial Developer. All Rights Reserved.
  2286.  *
  2287.  * Contributor(s):
  2288.  *   Fritz Schneider <fritz@google.com> (original author)
  2289.  *
  2290.  * Alternatively, the contents of this file may be used under the terms of
  2291.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2292.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2293.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2294.  * of those above. If you wish to allow use of your version of this file only
  2295.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2296.  * use your version of this file under the terms of the MPL, indicate your
  2297.  * decision by deleting the provisions above and replace them with the notice
  2298.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2299.  * the provisions above, a recipient may use your version of this file under
  2300.  * the terms of any one of the MPL, the GPL or the LGPL.
  2301.  *
  2302.  * ***** END LICENSE BLOCK ***** */
  2303.  
  2304.  
  2305. // A very thin wrapper around nsICryptoHash. It's not strictly
  2306. // necessary, but makes the code a bit cleaner and gives us the
  2307. // opportunity to verify that our implementations give the results that
  2308. // we expect, for example if we have to interoperate with a server.
  2309. //
  2310. // The digest* methods reset the state of the hasher, so it's
  2311. // necessary to call init() explicitly after them.
  2312. //
  2313. // Works only in Firefox 1.5+.
  2314. //
  2315. // IMPORTANT NOTE: Due to https://bugzilla.mozilla.org/show_bug.cgi?id=321024
  2316. // you cannot use the cryptohasher before app-startup. The symptom of doing
  2317. // so is a segfault in NSS.
  2318.  
  2319. /**
  2320.  * Instantiate a new hasher. You must explicitly call init() before use!
  2321.  */
  2322. function G_CryptoHasher() {
  2323.   this.debugZone = "cryptohasher";
  2324.   this.decoder_ = new G_Base64();
  2325.   this.hasher_ = null;
  2326. }
  2327.  
  2328. G_CryptoHasher.algorithms = {
  2329.   MD2: Ci.nsICryptoHash.MD2,
  2330.   MD5: Ci.nsICryptoHash.MD5,
  2331.   SHA1: Ci.nsICryptoHash.SHA1,
  2332.   SHA256: Ci.nsICryptoHash.SHA256,
  2333.   SHA384: Ci.nsICryptoHash.SHA384,
  2334.   SHA512: Ci.nsICryptoHash.SHA512,
  2335. };
  2336.  
  2337. /**
  2338.  * Initialize the hasher. This function must be called after every call
  2339.  * to one of the digest* methods.
  2340.  *
  2341.  * @param algorithm Constant from G_CryptoHasher.algorithms specifying the
  2342.  *                  algorithm this hasher will use
  2343.  */ 
  2344. G_CryptoHasher.prototype.init = function(algorithm) {
  2345.   var validAlgorithm = false;
  2346.   for (var alg in G_CryptoHasher.algorithms)
  2347.     if (algorithm == G_CryptoHasher.algorithms[alg])
  2348.       validAlgorithm = true;
  2349.  
  2350.   if (!validAlgorithm)
  2351.     throw new Error("Invalid algorithm: " + algorithm);
  2352.  
  2353.   this.hasher_ = Cc["@mozilla.org/security/hash;1"]
  2354.                  .createInstance(Ci.nsICryptoHash);
  2355.   this.hasher_.init(algorithm);
  2356. }
  2357.  
  2358. /**
  2359.  * Update the hash's internal state with input given in a string. Can be
  2360.  * called multiple times for incrementeal hash updates. Note that this function
  2361.  * is slllloooowww since it uses the a javascript implementation to convert the
  2362.  * string to an array. If you need something faster, use updateFromStream() with
  2363.  * an XPCOM stream.
  2364.  *
  2365.  * @param input String containing data to hash.
  2366.  */ 
  2367. G_CryptoHasher.prototype.updateFromString = function(input) {
  2368.   if (!this.hasher_)
  2369.     throw new Error("You must initialize the hasher first!");
  2370.  
  2371.   this.hasher_.update(this.decoder_.arrayifyString(input), input.length);
  2372. }
  2373.  
  2374. /**
  2375.  * Update the hash's internal state with input given in an array. Can be
  2376.  * called multiple times for incremental hash updates.
  2377.  *
  2378.  * @param input Array containing data to hash.
  2379.  */ 
  2380. G_CryptoHasher.prototype.updateFromArray = function(input) {
  2381.   if (!this.hasher_)
  2382.     throw new Error("You must initialize the hasher first!");
  2383.  
  2384.   this.hasher_.update(input, input.length);
  2385. }
  2386.  
  2387. /**
  2388.  * Update the hash's internal state with input given in a stream. Can be
  2389.  * called multiple times from incremental hash updates.
  2390.  */
  2391. G_CryptoHasher.prototype.updateFromStream = function(stream) {
  2392.   if (!this.hasher_)
  2393.     throw new Error("You must initialize the hasher first!");
  2394.  
  2395.   this.hasher_.updateFromStream(stream, stream.available());
  2396. }
  2397.  
  2398. /**
  2399.  * @returns The hash value as a string (sequence of 8-bit values)
  2400.  */ 
  2401. G_CryptoHasher.prototype.digestRaw = function() {
  2402.   var digest = this.hasher_.finish(false /* not b64 encoded */);
  2403.   this.hasher_ = null;
  2404.   return digest;
  2405. }
  2406.  
  2407. /**
  2408.  * @returns The hash value as a base64-encoded string
  2409.  */ 
  2410. G_CryptoHasher.prototype.digestBase64 = function() {
  2411.   var digest = this.hasher_.finish(true /* b64 encoded */);
  2412.   this.hasher_ = null;
  2413.   return digest;
  2414. }
  2415.  
  2416. /**
  2417.  * @returns The hash value as a hex-encoded string
  2418.  */ 
  2419. G_CryptoHasher.prototype.digestHex = function() {
  2420.   var raw = this.digestRaw();
  2421.   return this.toHex_(raw);
  2422. }
  2423.  
  2424. /**
  2425.  * Converts a sequence of values to a hex-encoded string. The input is a
  2426.  * a string, so you can stick 16-bit values in each character.
  2427.  *
  2428.  * @param str String to conver to hex. (Often this is just a sequence of
  2429.  *            16-bit values)
  2430.  *
  2431.  * @returns String containing the hex representation of the input
  2432.  */ 
  2433. G_CryptoHasher.prototype.toHex_ = function(str) {
  2434.   var hexchars = '0123456789ABCDEF';
  2435.   var hexrep = new Array(str.length * 2);
  2436.  
  2437.   for (var i = 0; i < str.length; ++i) {
  2438.     hexrep[i * 2] = hexchars.charAt((str.charCodeAt(i) >> 4) & 15);
  2439.     hexrep[i * 2 + 1] = hexchars.charAt(str.charCodeAt(i) & 15);
  2440.   }
  2441.   return hexrep.join('');
  2442. }
  2443.  
  2444. /* ***** BEGIN LICENSE BLOCK *****
  2445.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2446.  *
  2447.  * The contents of this file are subject to the Mozilla Public License Version
  2448.  * 1.1 (the "License"); you may not use this file except in compliance with
  2449.  * the License. You may obtain a copy of the License at
  2450.  * http://www.mozilla.org/MPL/
  2451.  *
  2452.  * Software distributed under the License is distributed on an "AS IS" basis,
  2453.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2454.  * for the specific language governing rights and limitations under the
  2455.  * License.
  2456.  *
  2457.  * The Original Code is Google Safe Browsing.
  2458.  *
  2459.  * The Initial Developer of the Original Code is Google Inc.
  2460.  * Portions created by the Initial Developer are Copyright (C) 2006
  2461.  * the Initial Developer. All Rights Reserved.
  2462.  *
  2463.  * Contributor(s):
  2464.  *   Aaron Boodman <aa@google.com> (original author)
  2465.  *
  2466.  * Alternatively, the contents of this file may be used under the terms of
  2467.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2468.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2469.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2470.  * of those above. If you wish to allow use of your version of this file only
  2471.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2472.  * use your version of this file under the terms of the MPL, indicate your
  2473.  * decision by deleting the provisions above and replace them with the notice
  2474.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2475.  * the provisions above, a recipient may use your version of this file under
  2476.  * the terms of any one of the MPL, the GPL or the LGPL.
  2477.  *
  2478.  * ***** END LICENSE BLOCK ***** */
  2479.  
  2480.  
  2481. // Firefox-specific additions to lib/js/lang.js.
  2482.  
  2483.  
  2484. /**
  2485.  * The always-useful alert. 
  2486.  */
  2487. function alert(msg, opt_title) {
  2488.   opt_title |= "message";
  2489.  
  2490.   Cc["@mozilla.org/embedcomp/prompt-service;1"]
  2491.     .getService(Ci.nsIPromptService)
  2492.     .alert(null, opt_title, msg.toString());
  2493. }
  2494.  
  2495.  
  2496. /**
  2497.  * The instanceof operator cannot be used on a pure js object to determine if 
  2498.  * it implements a certain xpcom interface. The QueryInterface method can, but
  2499.  * it throws an error which makes things more complex.
  2500.  */
  2501. function jsInstanceOf(obj, iid) {
  2502.   try {
  2503.     obj.QueryInterface(iid);
  2504.     return true;
  2505.   } catch (e) {
  2506.     if (e == Components.results.NS_ERROR_NO_INTERFACE) {
  2507.       return false;
  2508.     } else {
  2509.       throw e;
  2510.     }
  2511.   }
  2512. }
  2513.  
  2514.  
  2515. /**
  2516.  * Unbelievably, Function inheritence is broken in chrome in Firefox
  2517.  * (still as of FFox 1.5b1). Hence if you're working in an extension
  2518.  * and not using the subscriptloader, you can't use the method
  2519.  * above. Instead, use this global function that does roughly the same
  2520.  * thing.
  2521.  *
  2522.  ***************************************************************************
  2523.  *   NOTE THE REVERSED ORDER OF FUNCTION AND OBJECT REFERENCES AS bind()   *
  2524.  ***************************************************************************
  2525.  * 
  2526.  * // Example to bind foo.bar():
  2527.  * var bound = BindToObject(bar, foo, "arg1", "arg2");
  2528.  * bound("arg3", "arg4");
  2529.  * 
  2530.  * @param func {string} Reference to the function to be bound
  2531.  *
  2532.  * @param obj {object} Specifies the object which |this| should point to
  2533.  * when the function is run. If the value is null or undefined, it will default
  2534.  * to the global object.
  2535.  *
  2536.  * @param opt_{...} Dummy optional arguments to make a jscompiler happy
  2537.  *
  2538.  * @returns {function} A partially-applied form of the speficied function.
  2539.  */
  2540. function BindToObject(func, obj, opt_A, opt_B, opt_C, opt_D, opt_E, opt_F) {
  2541.   // This is the sick product of Aaron's mind. Not for the feint of heart.
  2542.   var args = Array.prototype.splice.call(arguments, 1, arguments.length);
  2543.   return Function.prototype.bind.apply(func, args);
  2544. }
  2545. /* ***** BEGIN LICENSE BLOCK *****
  2546.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2547.  *
  2548.  * The contents of this file are subject to the Mozilla Public License Version
  2549.  * 1.1 (the "License"); you may not use this file except in compliance with
  2550.  * the License. You may obtain a copy of the License at
  2551.  * http://www.mozilla.org/MPL/
  2552.  *
  2553.  * Software distributed under the License is distributed on an "AS IS" basis,
  2554.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2555.  * for the specific language governing rights and limitations under the
  2556.  * License.
  2557.  *
  2558.  * The Original Code is Google Safe Browsing.
  2559.  *
  2560.  * The Initial Developer of the Original Code is Google Inc.
  2561.  * Portions created by the Initial Developer are Copyright (C) 2006
  2562.  * the Initial Developer. All Rights Reserved.
  2563.  *
  2564.  * Contributor(s):
  2565.  *   Fritz Schneider <fritz@google.com> (original author)
  2566.  *
  2567.  * Alternatively, the contents of this file may be used under the terms of
  2568.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2569.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2570.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2571.  * of those above. If you wish to allow use of your version of this file only
  2572.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2573.  * use your version of this file under the terms of the MPL, indicate your
  2574.  * decision by deleting the provisions above and replace them with the notice
  2575.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2576.  * the provisions above, a recipient may use your version of this file under
  2577.  * the terms of any one of the MPL, the GPL or the LGPL.
  2578.  *
  2579.  * ***** END LICENSE BLOCK ***** */
  2580.  
  2581.  
  2582. // ObjectSafeMap is, shockingly, a Map with which it is safe to use
  2583. // objects as keys. It currently uses parallel arrays for storage,
  2584. // rendering it inefficient (linear) for large maps. We can always
  2585. // swap out the implementation if this becomes a problem. Note that
  2586. // this class uses strict equality to determine equivalent keys.
  2587. // 
  2588. // Interface:
  2589. //
  2590. //   insert(key, value)
  2591. //   erase(key)          // Returns true if key erased, false if not found
  2592. //   find(key)           // Returns undefined if key not found
  2593. //   replace(otherMap)   // Clones otherMap, replacing the current map
  2594. //   forEach(func)
  2595. //   size()              // Returns number of items in the map
  2596. //
  2597. // TODO: should probably make it iterable by implementing getList();
  2598.  
  2599.  
  2600. /**
  2601.  * Create a new ObjectSafeMap.
  2602.  *
  2603.  * @param opt_name A string used to name the map
  2604.  *
  2605.  * @constructor
  2606.  */
  2607. function G_ObjectSafeMap(opt_name) {
  2608.   this.debugZone = "objectsafemap";
  2609.   this.name_ = opt_name ? opt_name : "noname";
  2610.   this.keys_ = [];
  2611.   this.values_ = [];
  2612. }
  2613.  
  2614. /**
  2615.  * Helper function to return the index of a key. 
  2616.  *
  2617.  * @param key An key to find
  2618.  *
  2619.  * @returns Index in the keys array where the key is found, -1 if not
  2620.  */
  2621. G_ObjectSafeMap.prototype.indexOfKey_ = function(key) {
  2622.   for (var i = 0; i < this.keys_.length; i++)
  2623.     if (this.keys_[i] === key)
  2624.       return i;
  2625.   return -1;
  2626. }
  2627.  
  2628. /**
  2629.  * Add an item
  2630.  *
  2631.  * @param key An key to add (overwrites previous key)
  2632.  *
  2633.  * @param value The value to store at that key
  2634.  */
  2635. G_ObjectSafeMap.prototype.insert = function(key, value) {
  2636.   if (key === null)
  2637.     throw new Error("Can't use null as a key");
  2638.   if (value === undefined)
  2639.     throw new Error("Can't store undefined values in this map");
  2640.  
  2641.   var i = this.indexOfKey_(key);
  2642.   if (i == -1) {
  2643.     this.keys_.push(key);
  2644.     this.values_.push(value);
  2645.   } else {
  2646.     this.keys_[i] = key;
  2647.     this.values_[i] = value;
  2648.   }
  2649.  
  2650.   G_Assert(this, this.keys_.length == this.values_.length, 
  2651.            "Different number of keys than values!");
  2652. }
  2653.  
  2654. /**
  2655.  * Remove a key from the map
  2656.  *
  2657.  * @param key The key to remove
  2658.  *
  2659.  * @returns Boolean indicating if the key was removed
  2660.  */
  2661. G_ObjectSafeMap.prototype.erase = function(key) {
  2662.   var keyLocation = this.indexOfKey_(key);
  2663.   var keyFound = keyLocation != -1;
  2664.   if (keyFound) {
  2665.     this.keys_.splice(keyLocation, 1);
  2666.     this.values_.splice(keyLocation, 1);
  2667.   }
  2668.   G_Assert(this, this.keys_.length == this.values_.length, 
  2669.            "Different number of keys than values!");
  2670.  
  2671.   return keyFound;
  2672. }
  2673.  
  2674. /**
  2675.  * Look up a key in the map
  2676.  *
  2677.  * @param key The key to look up
  2678.  * 
  2679.  * @returns The value at that key or undefined if it doesn't exist
  2680.  */
  2681. G_ObjectSafeMap.prototype.find = function(key) {
  2682.   var keyLocation = this.indexOfKey_(key);
  2683.   return keyLocation == -1 ? undefined : this.values_[keyLocation];
  2684. }
  2685.  
  2686. /**
  2687.  * Replace one map with the content of another
  2688.  *
  2689.  * @param map input map that needs to be merged into our map
  2690.  */
  2691. G_ObjectSafeMap.prototype.replace = function(other) {
  2692.   this.keys_ = [];
  2693.   this.values_ = [];
  2694.   for (var i = 0; i < other.keys_.length; i++) {
  2695.     this.keys_.push(other.keys_[i]);
  2696.     this.values_.push(other.values_[i]);
  2697.   }
  2698.  
  2699.   G_Assert(this, this.keys_.length == this.values_.length, 
  2700.            "Different number of keys than values!");
  2701. }
  2702.  
  2703. /**
  2704.  * Apply a function to each of the key value pairs.
  2705.  *
  2706.  * @param func Function to apply to the map's key value pairs
  2707.  */
  2708. G_ObjectSafeMap.prototype.forEach = function(func) {
  2709.   if (typeof func != "function")
  2710.     throw new Error("argument to forEach is not a function, it's a(n) " + 
  2711.                     typeof func);
  2712.  
  2713.   for (var i = 0; i < this.keys_.length; i++)
  2714.     func(this.keys_[i], this.values_[i]);
  2715. }
  2716.  
  2717. /**
  2718.  * @returns The number of keys in the map
  2719.  */
  2720. G_ObjectSafeMap.prototype.size = function() {
  2721.   return this.keys_.length;
  2722. }
  2723.  
  2724. /* ***** BEGIN LICENSE BLOCK *****
  2725.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2726.  *
  2727.  * The contents of this file are subject to the Mozilla Public License Version
  2728.  * 1.1 (the "License"); you may not use this file except in compliance with
  2729.  * the License. You may obtain a copy of the License at
  2730.  * http://www.mozilla.org/MPL/
  2731.  *
  2732.  * Software distributed under the License is distributed on an "AS IS" basis,
  2733.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2734.  * for the specific language governing rights and limitations under the
  2735.  * License.
  2736.  *
  2737.  * The Original Code is Google Safe Browsing.
  2738.  *
  2739.  * The Initial Developer of the Original Code is Google Inc.
  2740.  * Portions created by the Initial Developer are Copyright (C) 2006
  2741.  * the Initial Developer. All Rights Reserved.
  2742.  *
  2743.  * Contributor(s):
  2744.  *   Fritz Schneider <fritz@google.com> (original author)
  2745.  *
  2746.  * Alternatively, the contents of this file may be used under the terms of
  2747.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2748.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2749.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2750.  * of those above. If you wish to allow use of your version of this file only
  2751.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2752.  * use your version of this file under the terms of the MPL, indicate your
  2753.  * decision by deleting the provisions above and replace them with the notice
  2754.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2755.  * the provisions above, a recipient may use your version of this file under
  2756.  * the terms of any one of the MPL, the GPL or the LGPL.
  2757.  *
  2758.  * ***** END LICENSE BLOCK ***** */
  2759.  
  2760.  
  2761. // A couple of classes to simplify creating observers. 
  2762. //
  2763. // // Example1:
  2764. //
  2765. // function doSomething() { ... }
  2766. // var observer = new G_ObserverWrapper(topic, doSomething);
  2767. // someObj.addObserver(topic, observer);
  2768. //
  2769. // // Example2: 
  2770. //
  2771. // function doSomething() { ... }
  2772. // new G_ObserverServiceObserver("profile-after-change", 
  2773. //                               doSomething,
  2774. //                               true /* run only once */);
  2775.  
  2776.  
  2777. /**
  2778.  * This class abstracts the admittedly simple boilerplate required of
  2779.  * an nsIObserver. It saves you the trouble of implementing the
  2780.  * indirection of your own observe() function.
  2781.  *
  2782.  * @param topic String containing the topic the observer will filter for
  2783.  *
  2784.  * @param observeFunction Reference to the function to call when the 
  2785.  *                        observer fires
  2786.  *
  2787.  * @constructor
  2788.  */
  2789. function G_ObserverWrapper(topic, observeFunction) {
  2790.   this.debugZone = "observer";
  2791.   this.topic_ = topic;
  2792.   this.observeFunction_ = observeFunction;
  2793. }
  2794.  
  2795. /**
  2796.  * XPCOM
  2797.  */
  2798. G_ObserverWrapper.prototype.QueryInterface = function(iid) {
  2799.   if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserver))
  2800.     return this;
  2801.   throw Components.results.NS_ERROR_NO_INTERFACE;
  2802. }
  2803.  
  2804. /**
  2805.  * Invoked by the thingy being observed
  2806.  */
  2807. G_ObserverWrapper.prototype.observe = function(subject, topic, data) {
  2808.   if (topic == this.topic_)
  2809.     this.observeFunction_(subject, topic, data);
  2810. }
  2811.  
  2812.  
  2813. /**
  2814.  * This class abstracts the admittedly simple boilerplate required of
  2815.  * observing an observerservice topic. It implements the indirection
  2816.  * required, and automatically registers to hear the topic.
  2817.  *
  2818.  * @param topic String containing the topic the observer will filter for
  2819.  *
  2820.  * @param observeFunction Reference to the function to call when the 
  2821.  *                        observer fires
  2822.  *
  2823.  * @param opt_onlyOnce Boolean indicating if the observer should unregister
  2824.  *                     after it has fired
  2825.  *
  2826.  * @constructor
  2827.  */
  2828. function G_ObserverServiceObserver(topic, observeFunction, opt_onlyOnce) {
  2829.   this.debugZone = "observerserviceobserver";
  2830.   this.topic_ = topic;
  2831.   this.observeFunction_ = observeFunction;
  2832.   this.onlyOnce_ = !!opt_onlyOnce;
  2833.   
  2834.   this.observer_ = new G_ObserverWrapper(this.topic_, 
  2835.                                          BindToObject(this.observe_, this));
  2836.   this.observerService_ = Cc["@mozilla.org/observer-service;1"]
  2837.                           .getService(Ci.nsIObserverService);
  2838.   this.observerService_.addObserver(this.observer_, this.topic_, false);
  2839. }
  2840.  
  2841. /**
  2842.  * Unregister the observer from the observerservice
  2843.  */
  2844. G_ObserverServiceObserver.prototype.unregister = function() {
  2845.   this.observerService_.removeObserver(this.observer_, this.topic_);
  2846.   this.observerService_ = null;
  2847. }
  2848.  
  2849. /**
  2850.  * Invoked by the observerservice
  2851.  */
  2852. G_ObserverServiceObserver.prototype.observe_ = function(subject, topic, data) {
  2853.   this.observeFunction_(subject, topic, data);
  2854.   if (this.onlyOnce_)
  2855.     this.unregister();
  2856. }
  2857.  
  2858. /* ***** BEGIN LICENSE BLOCK *****
  2859.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2860.  *
  2861.  * The contents of this file are subject to the Mozilla Public License Version
  2862.  * 1.1 (the "License"); you may not use this file except in compliance with
  2863.  * the License. You may obtain a copy of the License at
  2864.  * http://www.mozilla.org/MPL/
  2865.  *
  2866.  * Software distributed under the License is distributed on an "AS IS" basis,
  2867.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2868.  * for the specific language governing rights and limitations under the
  2869.  * License.
  2870.  *
  2871.  * The Original Code is Google Safe Browsing.
  2872.  *
  2873.  * The Initial Developer of the Original Code is Google Inc.
  2874.  * Portions created by the Initial Developer are Copyright (C) 2006
  2875.  * the Initial Developer. All Rights Reserved.
  2876.  *
  2877.  * Contributor(s):
  2878.  *   Fritz Schneider <fritz@google.com> (original author)
  2879.  *
  2880.  * Alternatively, the contents of this file may be used under the terms of
  2881.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2882.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2883.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2884.  * of those above. If you wish to allow use of your version of this file only
  2885.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2886.  * use your version of this file under the terms of the MPL, indicate your
  2887.  * decision by deleting the provisions above and replace them with the notice
  2888.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2889.  * the provisions above, a recipient may use your version of this file under
  2890.  * the terms of any one of the MPL, the GPL or the LGPL.
  2891.  *
  2892.  * ***** END LICENSE BLOCK ***** */
  2893.  
  2894.  
  2895. // A helper class that knows how to parse from and serialize to
  2896. // protocol4. This is a simple, historical format used by some Google
  2897. // interfaces, for example the Toolbar (i.e., ancient services).
  2898. //
  2899. // Protocol4 consists of a newline-separated sequence of name/value
  2900. // pairs (strings). Each line consists of the name, the value length,
  2901. // and the value itself, all separated by colons. Example:
  2902. //
  2903. // foo:6:barbaz\n
  2904. // fritz:33:issickofdynamicallytypedlanguages\n
  2905.  
  2906.  
  2907. /**
  2908.  * This class knows how to serialize/deserialize maps to/from their
  2909.  * protocol4 representation.
  2910.  *
  2911.  * @constructor
  2912.  */
  2913. function G_Protocol4Parser() {
  2914.   this.debugZone = "protocol4";
  2915.  
  2916.   this.protocol4RegExp_ = new RegExp("([^:]+):\\d+:(.*)$");
  2917.   this.newlineRegExp_ = new RegExp("(\\r)?\\n");
  2918. }
  2919.  
  2920. /**
  2921.  * Create a map from a protocol4 string. Silently skips invalid lines.
  2922.  *
  2923.  * @param text String holding the protocol4 representation
  2924.  * 
  2925.  * @returns Object as an associative array with keys and values 
  2926.  *          given in text. The empty object is returned if none
  2927.  *          are parsed.
  2928.  */
  2929. G_Protocol4Parser.prototype.parse = function(text) {
  2930.  
  2931.   var response = {};
  2932.   if (!text)
  2933.     return response;
  2934.  
  2935.   // Responses are protocol4: (repeated) name:numcontentbytes:content\n
  2936.   var lines = text.split(this.newlineRegExp_);
  2937.   for (var i = 0; i < lines.length; i++)
  2938.     if (this.protocol4RegExp_.exec(lines[i]))
  2939.       response[RegExp.$1] = RegExp.$2;
  2940.  
  2941.   return response;
  2942. }
  2943.  
  2944. /**
  2945.  * Create a protocol4 string from a map (object). Throws an error on 
  2946.  * an invalid input.
  2947.  *
  2948.  * @param map Object as an associative array with keys and values 
  2949.  *            given as strings.
  2950.  *
  2951.  * @returns text String holding the protocol4 representation
  2952.  */
  2953. G_Protocol4Parser.prototype.serialize = function(map) {
  2954.   if (typeof map != "object")
  2955.     throw new Error("map must be an object");
  2956.  
  2957.   var text = "";
  2958.   for (var key in map) {
  2959.     if (typeof map[key] != "string")
  2960.       throw new Error("Keys and values must be strings");
  2961.     
  2962.     text += key + ":" + map[key].length + ":" + map[key] + "\n";
  2963.   }
  2964.   
  2965.   return text;
  2966. }
  2967.  
  2968. //@line 59 "/cygdrive/K/tinderbuild/src/flock/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"
  2969.  
  2970. /* ***** BEGIN LICENSE BLOCK *****
  2971.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2972.  *
  2973.  * The contents of this file are subject to the Mozilla Public License Version
  2974.  * 1.1 (the "License"); you may not use this file except in compliance with
  2975.  * the License. You may obtain a copy of the License at
  2976.  * http://www.mozilla.org/MPL/
  2977.  *
  2978.  * Software distributed under the License is distributed on an "AS IS" basis,
  2979.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2980.  * for the specific language governing rights and limitations under the
  2981.  * License.
  2982.  *
  2983.  * The Original Code is Google Safe Browsing.
  2984.  *
  2985.  * The Initial Developer of the Original Code is Google Inc.
  2986.  * Portions created by the Initial Developer are Copyright (C) 2006
  2987.  * the Initial Developer. All Rights Reserved.
  2988.  *
  2989.  * Contributor(s):
  2990.  *   Fritz Schneider <fritz@google.com> (original author)
  2991.  *
  2992.  * Alternatively, the contents of this file may be used under the terms of
  2993.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2994.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2995.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2996.  * of those above. If you wish to allow use of your version of this file only
  2997.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2998.  * use your version of this file under the terms of the MPL, indicate your
  2999.  * decision by deleting the provisions above and replace them with the notice
  3000.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3001.  * the provisions above, a recipient may use your version of this file under
  3002.  * the terms of any one of the MPL, the GPL or the LGPL.
  3003.  *
  3004.  * ***** END LICENSE BLOCK ***** */
  3005.  
  3006. // TODO: We don't use this class very much.  Try to use native nsIFile instead
  3007. //       and remove this file.
  3008.  
  3009. /**
  3010.  * A simple helper class that enables us to get or create the
  3011.  * directory in which our app will store stuff.
  3012.  */
  3013. function PROT_ApplicationDirectory() {
  3014.   this.debugZone = "appdir";
  3015.   this.appDir_ = G_File.getProfileFile();
  3016.   G_Debug(this, "Application directory is " + this.appDir_.path);
  3017. }
  3018.  
  3019. /**
  3020.  * @returns Boolean indicating if the directory exists
  3021.  */
  3022. PROT_ApplicationDirectory.prototype.exists = function() {
  3023.   return this.appDir_.exists() && this.appDir_.isDirectory();
  3024. }
  3025.  
  3026. /**
  3027.  * Creates the directory
  3028.  */
  3029. PROT_ApplicationDirectory.prototype.create = function() {
  3030.   G_Debug(this, "Creating app directory: " + this.appDir_.path);
  3031.   try {
  3032.     this.appDir_.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
  3033.   } catch(e) {
  3034.     G_Error(this, this.appDir_.path + " couldn't be created.");
  3035.   }
  3036. }
  3037.  
  3038. /**
  3039.  * @returns The nsIFile interface of the directory
  3040.  */
  3041. PROT_ApplicationDirectory.prototype.getAppDirFileInterface = function() {
  3042.   return this.appDir_;
  3043. }
  3044. /* ***** BEGIN LICENSE BLOCK *****
  3045.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3046.  *
  3047.  * The contents of this file are subject to the Mozilla Public License Version
  3048.  * 1.1 (the "License"); you may not use this file except in compliance with
  3049.  * the License. You may obtain a copy of the License at
  3050.  * http://www.mozilla.org/MPL/
  3051.  *
  3052.  * Software distributed under the License is distributed on an "AS IS" basis,
  3053.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3054.  * for the specific language governing rights and limitations under the
  3055.  * License.
  3056.  *
  3057.  * The Original Code is Google Safe Browsing.
  3058.  *
  3059.  * The Initial Developer of the Original Code is Google Inc.
  3060.  * Portions created by the Initial Developer are Copyright (C) 2006
  3061.  * the Initial Developer. All Rights Reserved.
  3062.  *
  3063.  * Contributor(s):
  3064.  *   Tony Chang <tc@google.com> (original author)
  3065.  *
  3066.  * Alternatively, the contents of this file may be used under the terms of
  3067.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3068.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3069.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3070.  * of those above. If you wish to allow use of your version of this file only
  3071.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3072.  * use your version of this file under the terms of the MPL, indicate your
  3073.  * decision by deleting the provisions above and replace them with the notice
  3074.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3075.  * the provisions above, a recipient may use your version of this file under
  3076.  * the terms of any one of the MPL, the GPL or the LGPL.
  3077.  *
  3078.  * ***** END LICENSE BLOCK ***** */
  3079.  
  3080. // This implements logic for stopping requests if the server starts to return
  3081. // too many errors.  If we get MAX_ERRORS errors in ERROR_PERIOD minutes, we
  3082. // back off for TIMEOUT_INCREMENT minutes.  If we get another error
  3083. // immediately after we restart, we double the timeout and add
  3084. // TIMEOUT_INCREMENT minutes, etc.
  3085. // 
  3086. // This is similar to the logic used by the search suggestion service.
  3087.  
  3088. // HTTP responses that count as an error.  We also include any 5xx response
  3089. // as an error.
  3090. const HTTP_FOUND                 = 302;
  3091. const HTTP_SEE_OTHER             = 303;
  3092. const HTTP_TEMPORARY_REDIRECT    = 307;
  3093.  
  3094. /**
  3095.  * @param maxErrors Number the number of errors needed to trigger backoff
  3096.  * @param errorPeriod Number time (ms) in which maxErros have to occur to
  3097.  *     trigger the backoff behavior
  3098.  * @param timeoutIncrement Number time (ms) the starting timeout period
  3099.  *     we double this time for consecutive errors
  3100.  * @param maxTimeout Number time (ms) maximum timeout period
  3101.  */
  3102. function RequestBackoff(maxErrors, errorPeriod, timeoutIncrement, maxTimeout) {
  3103.   this.MAX_ERRORS_ = maxErrors;
  3104.   this.ERROR_PERIOD_ = errorPeriod;
  3105.   this.TIMEOUT_INCREMENT_ = timeoutIncrement;
  3106.   this.MAX_TIMEOUT_ = maxTimeout;
  3107.  
  3108.   // Queue of ints keeping the time of errors.
  3109.   this.errorTimes_ = [];
  3110.   this.errorTimeout_ = 0;
  3111.   this.nextRequestTime_ = 0;
  3112.   this.backoffTriggered_ = false;
  3113. }
  3114.  
  3115. /**
  3116.  * Reset the object for reuse.
  3117.  */
  3118. RequestBackoff.prototype.reset = function() {
  3119.   this.errorTimes_ = [];
  3120.   this.errorTimeout_ = 0;
  3121.   this.nextRequestTime_ = 0;
  3122.   this.backoffTriggered_ = false;
  3123. }
  3124.  
  3125. /**
  3126.  * Check to see if we can make a request.
  3127.  */
  3128. RequestBackoff.prototype.canMakeRequest = function() {
  3129.   return Date.now() > this.nextRequestTime_;
  3130. }
  3131.  
  3132. /**
  3133.  * Notify this object of the last server response.  If it's an error,
  3134.  */
  3135. RequestBackoff.prototype.noteServerResponse = function(status) {
  3136.   if (this.isErrorStatus_(status)) {
  3137.     var now = Date.now();
  3138.     this.errorTimes_.push(now);
  3139.  
  3140.     // We only care about keeping track of MAX_ERRORS
  3141.     if (this.errorTimes_.length > this.MAX_ERRORS_)
  3142.       this.errorTimes_.shift();
  3143.  
  3144.     // See if we hit the backoff case
  3145.     // This either means we hit MAX_ERRORS in ERROR_PERIOD
  3146.     // *or* we were already in a backoff state, in which case we
  3147.     // increase our timeout.
  3148.     if ((this.errorTimes_.length == this.MAX_ERRORS_ &&
  3149.          now - this.errorTimes_[0] < this.ERROR_PERIOD_)
  3150.         || this.backoffTriggered_) {
  3151.       this.errorTimeout_ = (this.errorTimeout_ * 2)  + this.TIMEOUT_INCREMENT_;
  3152.       this.errorTimeout_ = Math.min(this.errorTimeout_, this.MAX_TIMEOUT_);
  3153.       this.nextRequestTime_ = now + this.errorTimeout_;
  3154.       this.backoffTriggered_ = true;
  3155.     }
  3156.   } else {
  3157.     // Reset error timeout, allow requests to go through, and switch out
  3158.     // of backoff state.
  3159.     this.errorTimeout_ = 0;
  3160.     this.nextRequestTime_ = 0;
  3161.     this.backoffTriggered_ = false;
  3162.   }
  3163. }
  3164.  
  3165. /**
  3166.  * We consider 302, 303, 307, and 5xx http responses to be errors.
  3167.  * @param status Number http status
  3168.  * @return Boolean true if we consider this http status an error
  3169.  */
  3170. RequestBackoff.prototype.isErrorStatus_ = function(status) {
  3171.   return ((500 <= status && status <= 599) ||
  3172.           HTTP_FOUND == status ||
  3173.           HTTP_SEE_OTHER == status ||
  3174.           HTTP_TEMPORARY_REDIRECT == status);
  3175. }
  3176.  
  3177. /* ***** BEGIN LICENSE BLOCK *****
  3178.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3179.  *
  3180.  * The contents of this file are subject to the Mozilla Public License Version
  3181.  * 1.1 (the "License"); you may not use this file except in compliance with
  3182.  * the License. You may obtain a copy of the License at
  3183.  * http://www.mozilla.org/MPL/
  3184.  *
  3185.  * Software distributed under the License is distributed on an "AS IS" basis,
  3186.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3187.  * for the specific language governing rights and limitations under the
  3188.  * License.
  3189.  *
  3190.  * The Original Code is Google Safe Browsing.
  3191.  *
  3192.  * The Initial Developer of the Original Code is Google Inc.
  3193.  * Portions created by the Initial Developer are Copyright (C) 2006
  3194.  * the Initial Developer. All Rights Reserved.
  3195.  *
  3196.  * Contributor(s):
  3197.  *   Fritz Schneider <fritz@google.com> (original author)
  3198.  *   Monica Chew <mmc@google.com>
  3199.  *
  3200.  * Alternatively, the contents of this file may be used under the terms of
  3201.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3202.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3203.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3204.  * of those above. If you wish to allow use of your version of this file only
  3205.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3206.  * use your version of this file under the terms of the MPL, indicate your
  3207.  * decision by deleting the provisions above and replace them with the notice
  3208.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3209.  * the provisions above, a recipient may use your version of this file under
  3210.  * the terms of any one of the MPL, the GPL or the LGPL.
  3211.  *
  3212.  * ***** END LICENSE BLOCK ***** */
  3213.  
  3214.  
  3215. // This file implements our query param encryption. You hand it a set
  3216. // of query params, and it will hand you a set of (maybe) encrypted
  3217. // query params back. It takes the query params you give it, 
  3218. // encodes and encrypts them into a encrypted query param, and adds
  3219. // the extra query params the server will need to decrypt them
  3220. // (e.g., the version of encryption and the decryption key).
  3221. // 
  3222. // The key manager provides the keys we need; this class just focuses
  3223. // on encrypting query params. See the url crypto key manager for
  3224. // details of our protocol, but essentially encryption is
  3225. // RC4_key(input) with key == MD5(K_C || nonce) where nonce is a
  3226. // 32-bit integer appended big-endian and K_C is the client's key.
  3227. //
  3228. // If for some reason we don't have an encryption key, encrypting is the 
  3229. // identity function.
  3230.  
  3231. /**
  3232.  * This class knows how to encrypt query parameters that will be
  3233.  * understood by the lookupserver.
  3234.  * 
  3235.  * @constructor
  3236.  */
  3237. function PROT_UrlCrypto() {
  3238.   this.debugZone = "urlcrypto";
  3239.   this.hasher_ = new G_CryptoHasher();
  3240.   this.base64_ = new G_Base64();
  3241.   this.streamCipher_ = Cc["@mozilla.org/security/streamcipher;1"]
  3242.                        .createInstance(Ci.nsIStreamCipher);
  3243.  
  3244.   if (!this.manager_) {
  3245.     // Create a UrlCryptoKeyManager to reads keys from profile directory if
  3246.     // one doesn't already exist.  UrlCryptoKeyManager puts a reference to
  3247.     // itself on PROT_UrlCrypto.prototype (this also prevents garbage
  3248.     // collection).
  3249.     new PROT_UrlCryptoKeyManager();
  3250.   }
  3251.  
  3252.   // Convenience properties
  3253.   this.VERSION = PROT_UrlCrypto.VERSION;
  3254.   this.RC4_DISCARD_BYTES = PROT_UrlCrypto.RC4_DISCARD_BYTES;
  3255.   this.VERSION_QUERY_PARAM_NAME = PROT_UrlCrypto.QPS.VERSION_QUERY_PARAM_NAME;
  3256.   this.ENCRYPTED_PARAMS_PARAM_NAME = 
  3257.     PROT_UrlCrypto.QPS.ENCRYPTED_PARAMS_PARAM_NAME;
  3258.   this.COUNT_QUERY_PARAM_NAME = PROT_UrlCrypto.QPS.COUNT_QUERY_PARAM_NAME;
  3259.   this.WRAPPEDKEY_QUERY_PARAM_NAME = 
  3260.     PROT_UrlCrypto.QPS.WRAPPEDKEY_QUERY_PARAM_NAME;
  3261.  
  3262.   // Properties for computing macs
  3263.   this.macer_ = new G_CryptoHasher(); // don't use hasher_
  3264.   this.macInitialized_ = false;
  3265.   // Separator to prevent leakage between key and data when computing mac
  3266.   this.separator_ = ":coolgoog:";
  3267.   this.separatorArray_ = this.base64_.arrayifyString(this.separator_);
  3268. }
  3269.  
  3270. // The version of encryption we implement
  3271. PROT_UrlCrypto.VERSION = "1";
  3272.  
  3273. PROT_UrlCrypto.RC4_DISCARD_BYTES = 1600;
  3274.  
  3275. // The query params are we going to send to let the server know what is
  3276. // encrypted, and how
  3277. PROT_UrlCrypto.QPS = {};
  3278. PROT_UrlCrypto.QPS.VERSION_QUERY_PARAM_NAME = "encver";
  3279. PROT_UrlCrypto.QPS.ENCRYPTED_PARAMS_PARAM_NAME = "encparams";
  3280. PROT_UrlCrypto.QPS.COUNT_QUERY_PARAM_NAME = "nonce";
  3281. PROT_UrlCrypto.QPS.WRAPPEDKEY_QUERY_PARAM_NAME = "wrkey";
  3282.  
  3283. /**
  3284.  * @returns Reference to the keymanager (if one exists), else undefined
  3285.  */
  3286. PROT_UrlCrypto.prototype.getManager = function() {
  3287.   return this.manager_;
  3288. }
  3289.  
  3290. /**
  3291.  * Helper method that takes a map of query params (param name ->
  3292.  * value) and turns them into a query string. Note that it encodes
  3293.  * the values as it writes the string.
  3294.  *
  3295.  * @param params Object (map) of query names to values. Values should
  3296.  *               not be uriencoded.
  3297.  *
  3298.  * @returns String of query params from the map. Values will be uri
  3299.  *          encoded
  3300.  */
  3301. PROT_UrlCrypto.prototype.appendParams_ = function(params) {
  3302.   var queryString = "";
  3303.   for (var param in params)
  3304.     queryString += "&" + param + "=" + encodeURIComponent(params[param]);
  3305.                    
  3306.   return queryString;
  3307. }
  3308.  
  3309. /**
  3310.  * Encrypt a set of query params if we can. If we can, we return a new
  3311.  * set of query params that should be added to a query string. The set
  3312.  * of query params WILL BE different than the input query params if we
  3313.  * can encrypt (e.g., there will be extra query params with meta-
  3314.  * information such as the version of encryption we're using). If we
  3315.  * can't encrypt, we just return the query params we're passed.
  3316.  *
  3317.  * @param params Object (map) of query param names to values. Values should
  3318.  *               not be uriencoded.
  3319.  *
  3320.  * @returns Object (map) of query param names to values. Values are NOT
  3321.  *          uriencoded; the caller should encode them as it writes them
  3322.  *          to a proper query string.
  3323.  */
  3324. PROT_UrlCrypto.prototype.maybeCryptParams = function(params) {
  3325.   if (!this.manager_)
  3326.     throw new Error("Need a key manager for UrlCrypto");
  3327.   if (typeof params != "object")
  3328.     throw new Error("params is an associative array of name/value params");
  3329.  
  3330.   var clientKeyArray = this.manager_.getClientKeyArray();
  3331.   var wrappedKey = this.manager_.getWrappedKey();
  3332.  
  3333.   // No keys? Can't encrypt. Damn.
  3334.   if (!clientKeyArray || !wrappedKey) {
  3335.     G_Debug(this, "No key; can't encrypt query params");
  3336.     return params;
  3337.   }
  3338.  
  3339.   // Serialize query params to a query string that we will then
  3340.   // encrypt and place in a special query param the front-end knows is
  3341.   // encrypted.
  3342.   var queryString = this.appendParams_(params);
  3343.   
  3344.   // Nonce, really. We want 32 bits; make it so.
  3345.   var counter = this.getCount_();
  3346.   counter = counter & 0xFFFFFFFF;
  3347.   
  3348.   var encrypted = this.encryptV1(clientKeyArray, 
  3349.                                  this.VERSION,
  3350.                                  counter,
  3351.                                  queryString);
  3352.  
  3353.   params = {};
  3354.   params[this.VERSION_QUERY_PARAM_NAME] = this.VERSION;
  3355.   params[this.COUNT_QUERY_PARAM_NAME] = counter;
  3356.   params[this.WRAPPEDKEY_QUERY_PARAM_NAME] = wrappedKey;
  3357.   params[this.ENCRYPTED_PARAMS_PARAM_NAME] = encrypted;
  3358.  
  3359.   return params;
  3360. }
  3361.  
  3362. /**
  3363.  * Encrypts text and returns a base64 string of the results.
  3364.  *
  3365.  * This method runs in about ~2ms on a 2Ghz P4. (Turn debugging off if
  3366.  * you see it much slower).
  3367.  *
  3368.  * @param clientKeyArray Array of bytes (numbers in [0,255]) composing K_C
  3369.  *
  3370.  * @param version String indicating the version of encryption we should use.
  3371.  *
  3372.  * @param counter Number that acts as a nonce for this encryption
  3373.  *
  3374.  * @param text String to be encrypted
  3375.  *
  3376.  * @returns String containing the websafe base64-encoded ciphertext
  3377.  */
  3378. PROT_UrlCrypto.prototype.encryptV1 = function(clientKeyArray,
  3379.                                               version, 
  3380.                                               counter,
  3381.                                               text) {
  3382.  
  3383.   // We're a version1 encrypter, after all
  3384.   if (version != "1") 
  3385.     throw new Error("Unknown encryption version");
  3386.  
  3387.   var key = this.deriveEncryptionKey(clientKeyArray, counter);
  3388.  
  3389.   this.streamCipher_.init(key);
  3390.  
  3391.   if (this.RC4_DISCARD_BYTES > 0)
  3392.     this.streamCipher_.discard(this.RC4_DISCARD_BYTES);
  3393.   
  3394.   this.streamCipher_.updateFromString(text);
  3395.  
  3396.   var encrypted = this.streamCipher_.finish(true /* base64 encoded */);
  3397.   // The base64 version we get has new lines, we want to remove those.
  3398.   
  3399.   return encrypted.replace(/\r\n/g, "");
  3400. }
  3401.   
  3402. /**
  3403.  * Create an encryption key from K_C and a nonce
  3404.  *
  3405.  * @param clientKeyArray Array of bytes comprising K_C
  3406.  *
  3407.  * @param count Number that acts as a nonce for this key
  3408.  *
  3409.  * @return nsIKeyObject
  3410.  */
  3411. PROT_UrlCrypto.prototype.deriveEncryptionKey = function(clientKeyArray, 
  3412.                                                         count) {
  3413.   G_Assert(this, clientKeyArray instanceof Array,
  3414.            "Client key should be an array of bytes");
  3415.   G_Assert(this, typeof count == "number", "Count should be a number");
  3416.   
  3417.   // Don't clobber the client key by appending the nonce; use another array
  3418.   var paddingArray = [];
  3419.   paddingArray.push(count >> 24);
  3420.   paddingArray.push((count >> 16) & 0xFF);
  3421.   paddingArray.push((count >> 8) & 0xFF);
  3422.   paddingArray.push(count & 0xFF);
  3423.  
  3424.   this.hasher_.init(G_CryptoHasher.algorithms.MD5);
  3425.   this.hasher_.updateFromArray(clientKeyArray);
  3426.   this.hasher_.updateFromArray(paddingArray);
  3427.  
  3428.   // Create the nsIKeyObject
  3429.   var keyFactory = Cc["@mozilla.org/security/keyobjectfactory;1"]
  3430.                    .getService(Ci.nsIKeyObjectFactory);
  3431.   var key = keyFactory.keyFromString(Ci.nsIKeyObject.RC4,
  3432.                                      this.hasher_.digestRaw());
  3433.   return key;
  3434. }
  3435.  
  3436. /**
  3437.  * Return a new nonce for us to use. Rather than keeping a counter and
  3438.  * the headaches that entails, just use the low ms since the epoch.
  3439.  *
  3440.  * @returns 32-bit number that is the nonce to use for this encryption
  3441.  */
  3442. PROT_UrlCrypto.prototype.getCount_ = function() {
  3443.   return ((new Date).getTime() & 0xFFFFFFFF);
  3444. }
  3445.  
  3446. /**
  3447.  * Init the mac.  This function is called by WireFormatReader if the update
  3448.  * server has sent along a mac param.  The caller must not call initMac again
  3449.  * before calling finishMac; instead, the caller should just use another
  3450.  * UrlCrypto object.
  3451.  *
  3452.  * @param opt_clientKeyArray Optional clientKeyArray, for testing
  3453.  */
  3454. PROT_UrlCrypto.prototype.initMac = function(opt_clientKeyArray) {
  3455.   if (this.macInitialized_) {
  3456.     throw new Error("Can't interleave calls to initMac.  Please use another " +
  3457.                     "UrlCrypto object.");
  3458.   }
  3459.  
  3460.   this.macInitialized_ = true;
  3461.  
  3462.   var clientKeyArray = null;
  3463.  
  3464.   if (!!opt_clientKeyArray) {
  3465.     clientKeyArray = opt_clientKeyArray;
  3466.   } else {
  3467.     clientKeyArray = this.manager_.getClientKeyArray();
  3468.   }
  3469.  
  3470.   // Don't re-use this.hasher_, in case someone calls deriveEncryptionKey
  3471.   // between initMac and finishMac
  3472.   this.macer_.init(G_CryptoHasher.algorithms.MD5);
  3473.  
  3474.   this.macer_.updateFromArray(clientKeyArray);
  3475.   this.macer_.updateFromArray(this.separatorArray_);
  3476. }
  3477.  
  3478. /**
  3479.  * Add a line to the mac.  Called by WireFormatReader.processLine.  Not thread
  3480.  * safe.
  3481.  *
  3482.  * @param s The string to add
  3483.  */
  3484. PROT_UrlCrypto.prototype.updateMacFromString = function(s) {
  3485.   if (!this.macInitialized_) {
  3486.     throw new Error ("Initialize mac first");
  3487.   }
  3488.  
  3489.   var arr = this.base64_.arrayifyString(s);
  3490.   this.macer_.updateFromArray(arr);
  3491. }
  3492.  
  3493. /**
  3494.  * Finish up computing the mac.  Not thread safe.
  3495.  *
  3496.  * @param opt_clientKeyArray Optional clientKeyArray, for testing
  3497.  */
  3498. PROT_UrlCrypto.prototype.finishMac = function(opt_clientKeyArray) {
  3499.   var clientKeyArray = null;
  3500.   if (!!opt_clientKeyArray) {
  3501.     clientKeyArray = opt_clientKeyArray;
  3502.   } else {
  3503.     clientKeyArray = this.manager_.getClientKeyArray();
  3504.   }
  3505.  
  3506.   if (!this.macInitialized_) {
  3507.     throw new Error ("Initialize mac first");
  3508.   }
  3509.   this.macer_.updateFromArray(this.separatorArray_);
  3510.   this.macer_.updateFromArray(clientKeyArray);
  3511.  
  3512.   this.macInitialized_ = false;
  3513.  
  3514.   return this.macer_.digestBase64();
  3515. }
  3516.  
  3517. /**
  3518.  * Compute a mac over the whole data string, and return the base64-encoded
  3519.  * string
  3520.  *
  3521.  * @param data A string
  3522.  * @param opt_outputRaw True for raw output, false for base64
  3523.  * @param opt_clientKeyArray An optional key to pass in for testing
  3524.  * @param opt_separatorArray An optional separator array to pass in for testing
  3525.  * @returns MD5(key+separator+data+separator+key)
  3526.  */
  3527. PROT_UrlCrypto.prototype.computeMac = function(data, 
  3528.                                                opt_outputRaw,
  3529.                                                opt_clientKeyArray,
  3530.                                                opt_separatorArray) {
  3531.   var clientKeyArray = null;
  3532.   var separatorArray = null;
  3533.  
  3534.   // Get keys and such for testing
  3535.   if (!!opt_clientKeyArray) {
  3536.     clientKeyArray = opt_clientKeyArray;
  3537.   } else {
  3538.     clientKeyArray = this.manager_.getClientKeyArray();
  3539.   }
  3540.  
  3541.   if (!!opt_separatorArray) {
  3542.     separatorArray = opt_separatorArray;
  3543.   } else {
  3544.     separatorArray = this.separatorArray_;
  3545.   }
  3546.  
  3547.   this.macer_.init(G_CryptoHasher.algorithms.MD5);
  3548.  
  3549.   this.macer_.updateFromArray(clientKeyArray);
  3550.   this.macer_.updateFromArray(separatorArray);
  3551.  
  3552.   // Note to self: calling G_CryptoHasher.updateFromString ain't the same as
  3553.   // arrayifying the string and then calling updateFromArray.  Not sure if
  3554.   // that's a bug in G_CryptoHasher or not.  Niels, what do you think?
  3555.   var arr = this.base64_.arrayifyString(data);
  3556.   this.macer_.updateFromArray(arr);
  3557.  
  3558.   this.macer_.updateFromArray(separatorArray);
  3559.   this.macer_.updateFromArray(clientKeyArray);
  3560.  
  3561.   if (!!opt_outputRaw) {
  3562.     return this.macer_.digestRaw();
  3563.   }
  3564.   return this.macer_.digestBase64();
  3565. }
  3566.  
  3567. /* ***** BEGIN LICENSE BLOCK *****
  3568.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3569.  *
  3570.  * The contents of this file are subject to the Mozilla Public License Version
  3571.  * 1.1 (the "License"); you may not use this file except in compliance with
  3572.  * the License. You may obtain a copy of the License at
  3573.  * http://www.mozilla.org/MPL/
  3574.  *
  3575.  * Software distributed under the License is distributed on an "AS IS" basis,
  3576.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3577.  * for the specific language governing rights and limitations under the
  3578.  * License.
  3579.  *
  3580.  * The Original Code is Google Safe Browsing.
  3581.  *
  3582.  * The Initial Developer of the Original Code is Google Inc.
  3583.  * Portions created by the Initial Developer are Copyright (C) 2006
  3584.  * the Initial Developer. All Rights Reserved.
  3585.  *
  3586.  * Contributor(s):
  3587.  *   Fritz Schneider <fritz@google.com> (original author)
  3588.  *
  3589.  * Alternatively, the contents of this file may be used under the terms of
  3590.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3591.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3592.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3593.  * of those above. If you wish to allow use of your version of this file only
  3594.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3595.  * use your version of this file under the terms of the MPL, indicate your
  3596.  * decision by deleting the provisions above and replace them with the notice
  3597.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3598.  * the provisions above, a recipient may use your version of this file under
  3599.  * the terms of any one of the MPL, the GPL or the LGPL.
  3600.  *
  3601.  * ***** END LICENSE BLOCK ***** */
  3602.  
  3603.  
  3604. // This file implements the tricky business of managing the keys for our 
  3605. // URL encryption. The protocol is:
  3606. //
  3607. // - Server generates secret key K_S
  3608. // - Client starts up and requests a new key K_C from the server via HTTPS
  3609. // - Server generates K_C and WrappedKey, which is K_C encrypted with K_S
  3610. // - Server resonse with K_C and WrappedKey
  3611. // - When client wants to encrypt a URL, it encrypts it with K_C and sends
  3612. //   the encrypted URL along with WrappedKey
  3613. // - Server decrypts WrappedKey with K_S to get K_C, and the URL with K_C
  3614. //
  3615. // This is, however, trickier than it sounds for two reasons. First,
  3616. // we want to keep the number of HTTPS requests to an aboslute minimum
  3617. // (like 1 or 2 per browser session). Second, the HTTPS request at
  3618. // startup might fail, for example the user might be offline or a URL
  3619. // fetch might need to be issued before the HTTPS request has
  3620. // completed.
  3621. //
  3622. // We implement the following policy:
  3623. // 
  3624. // - Firefox will issue at most two HTTPS getkey requests per session
  3625. // - Firefox will issue one HTTPS getkey request at startup if more than 24
  3626. //   hours has passed since the last getkey request.
  3627. // - Firefox will serialize to disk any key it gets
  3628. // - Firefox will fall back on this serialized key until it has a
  3629. //   fresh key
  3630. // - The front-end can respond with a flag in a lookup request that tells
  3631. //   the client to re-key. Firefox will issue a new HTTPS getkey request
  3632. //   at this time if it has only issued one before
  3633.  
  3634. // We store the user key in this file.  The key can be used to verify signed
  3635. // server updates.
  3636. const kKeyFilename = "kf.txt";
  3637.  
  3638. /**
  3639.  * A key manager for UrlCrypto. There should be exactly one of these
  3640.  * per appplication, and all UrlCrypto's should share it. This is
  3641.  * currently implemented by having the manager attach itself to the
  3642.  * UrlCrypto's prototype at startup. We could've opted for a global
  3643.  * instead, but I like this better, even though it is spooky action
  3644.  * at a distance.
  3645.  * XXX: Should be an XPCOM service
  3646.  *
  3647.  * @param opt_keyFilename String containing the name of the 
  3648.  *                        file we should serialize keys to/from. Used
  3649.  *                        mostly for testing.
  3650.  *
  3651.  * @param opt_testing Boolean indicating whether we are testing. If we 
  3652.  *                    are, then we skip trying to read the old key from
  3653.  *                    file and automatically trying to rekey; presumably
  3654.  *                    the tester will drive these manually.
  3655.  *
  3656.  * @constructor
  3657.  */
  3658. function PROT_UrlCryptoKeyManager(opt_keyFilename, opt_testing) {
  3659.   this.debugZone = "urlcryptokeymanager";
  3660.   this.testing_ = !!opt_testing;
  3661.   this.base64_ = new G_Base64();
  3662.   this.clientKey_ = null;          // Base64-encoded, as fetched from server
  3663.   this.clientKeyArray_ = null;     // Base64-decoded into an array of numbers
  3664.   this.wrappedKey_ = null;         // Opaque websafe base64-encoded server key
  3665.   this.rekeyTries_ = 0;
  3666.  
  3667.   // Don't do anything until keyUrl_ is set.
  3668.   this.keyUrl_ = null;
  3669.  
  3670.   this.keyFilename_ = opt_keyFilename ? 
  3671.                       opt_keyFilename : kKeyFilename;
  3672.  
  3673.   // Convenience properties
  3674.   this.MAX_REKEY_TRIES = PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES;
  3675.   this.CLIENT_KEY_NAME = PROT_UrlCryptoKeyManager.CLIENT_KEY_NAME;
  3676.   this.WRAPPED_KEY_NAME = PROT_UrlCryptoKeyManager.WRAPPED_KEY_NAME;
  3677.  
  3678.   if (!this.testing_) {
  3679.     G_Assert(this, !PROT_UrlCrypto.prototype.manager_,
  3680.              "Already have manager?");
  3681.     PROT_UrlCrypto.prototype.manager_ = this;
  3682.  
  3683.     this.maybeLoadOldKey();
  3684.   }
  3685. }
  3686.  
  3687. // Do ***** NOT ***** set this higher; HTTPS is expensive
  3688. PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES = 2;
  3689.  
  3690. // Base pref for keeping track of when we updated our key.
  3691. // We store the time as seconds since the epoch.
  3692. PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF = "urlclassifier.keyupdatetime.";
  3693.  
  3694. // Once a day (interval in seconds)
  3695. PROT_UrlCryptoKeyManager.KEY_MIN_UPDATE_TIME = 24 * 60 * 60;
  3696.  
  3697. // These are the names the server will respond with in protocol4 format
  3698. PROT_UrlCryptoKeyManager.CLIENT_KEY_NAME = "clientkey";
  3699. PROT_UrlCryptoKeyManager.WRAPPED_KEY_NAME = "wrappedkey";
  3700.  
  3701.  
  3702. /**
  3703.  * Called by a UrlCrypto to get the current K_C
  3704.  *
  3705.  * @returns Array of numbers making up the client key or null if we 
  3706.  *          have no key
  3707.  */
  3708. PROT_UrlCryptoKeyManager.prototype.getClientKeyArray = function() {
  3709.   return this.clientKeyArray_;
  3710. }
  3711.  
  3712. /**
  3713.  * Called by a UrlCrypto to get WrappedKey
  3714.  *
  3715.  * @returns Opaque base64-encoded WrappedKey or null if we haven't
  3716.  *          gotten one
  3717.  */
  3718. PROT_UrlCryptoKeyManager.prototype.getWrappedKey = function() {
  3719.   return this.wrappedKey_;
  3720. }
  3721.  
  3722. /**
  3723.  * Change the key url.  When we do this, we go ahead and rekey.
  3724.  * @param keyUrl String
  3725.  */
  3726. PROT_UrlCryptoKeyManager.prototype.setKeyUrl = function(keyUrl) {
  3727.   // If it's the same key url, do nothing.
  3728.   if (keyUrl == this.keyUrl_)
  3729.     return;
  3730.  
  3731.   this.keyUrl_ = keyUrl;
  3732.   this.rekeyTries_ = 0;
  3733.  
  3734.   // Check to see if we should make a new getkey request.
  3735.   var prefs = new G_Preferences(PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF);
  3736.   var nextRekey = prefs.getPref(this.getPrefName_(this.keyUrl_), 0);
  3737.   if (nextRekey < parseInt(Date.now() / 1000, 10)) {
  3738.     this.reKey();
  3739.   }
  3740. }
  3741.  
  3742. /**
  3743.  * Given a url, return the pref value to use (pref contains last update time).
  3744.  * We basically use the url up until query parameters.  This avoids duplicate
  3745.  * pref entries as version number changes over time.
  3746.  * @param url String getkey URL
  3747.  */
  3748. PROT_UrlCryptoKeyManager.prototype.getPrefName_ = function(url) {
  3749.   var queryParam = url.indexOf("?");
  3750.   if (queryParam != -1) {
  3751.     return url.substring(0, queryParam);
  3752.   }
  3753.   return url;
  3754. }
  3755.  
  3756. /**
  3757.  * Tell the manager to re-key. For safety, this method still obeys the
  3758.  * max-tries limit. Clients should generally use maybeReKey() if they
  3759.  * want to try a re-keying: it's an error to call reKey() after we've
  3760.  * hit max-tries, but not an error to call maybeReKey().
  3761.  */
  3762. PROT_UrlCryptoKeyManager.prototype.reKey = function() {
  3763.   
  3764.   if (this.rekeyTries_ > this.MAX_REKEY_TRIES)
  3765.     throw new Error("Have already rekeyed " + this.rekeyTries_ + " times");
  3766.  
  3767.   this.rekeyTries_++;
  3768.  
  3769.   G_Debug(this, "Attempting to re-key");
  3770.   // If the keyUrl isn't set, we don't do anything.
  3771.   if (!this.testing_ && this.keyUrl_) {
  3772.     (new PROT_XMLFetcher()).get(this.keyUrl_,
  3773.                                 BindToObject(this.onGetKeyResponse, this));
  3774.  
  3775.     // Calculate the next time we're allowed to re-key.
  3776.     var prefs = new G_Preferences(PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF);
  3777.     var nextRekey = parseInt(Date.now() / 1000, 10)
  3778.                   + PROT_UrlCryptoKeyManager.KEY_MIN_UPDATE_TIME;
  3779.     prefs.setPref(this.getPrefName_(this.keyUrl_), nextRekey);
  3780.   }
  3781. }
  3782.  
  3783. /**
  3784.  * Try to re-key if we haven't already hit our limit. It's OK to call
  3785.  * this method multiple times, even if we've already tried to rekey
  3786.  * more than the max. It will simply refuse to do so.
  3787.  *
  3788.  * @returns Boolean indicating if it actually issued a rekey request (that
  3789.  *          is, if we haven' already hit the max)
  3790.  */
  3791. PROT_UrlCryptoKeyManager.prototype.maybeReKey = function() {
  3792.   if (this.rekeyTries_ > this.MAX_REKEY_TRIES) {
  3793.     G_Debug(this, "Not re-keying; already at max");
  3794.     return false;
  3795.   }
  3796.  
  3797.   this.reKey();
  3798.   return true;
  3799. }
  3800.  
  3801. /**
  3802.  * @returns Boolean indicating if we have a key we can use 
  3803.  */
  3804. PROT_UrlCryptoKeyManager.prototype.hasKey_ = function() {
  3805.   return this.clientKey_ != null && this.wrappedKey_ != null;
  3806. }
  3807.  
  3808. /**
  3809.  * Set a new key and serialize it to disk.
  3810.  *
  3811.  * @param clientKey String containing the base64-encoded client key 
  3812.  *                  we wish to use
  3813.  *
  3814.  * @param wrappedKey String containing the opaque base64-encoded WrappedKey
  3815.  *                   the server gave us (i.e., K_C encrypted with K_S)
  3816.  */
  3817. PROT_UrlCryptoKeyManager.prototype.replaceKey_ = function(clientKey, 
  3818.                                                           wrappedKey) {
  3819.   if (this.clientKey_)
  3820.     G_Debug(this, "Replacing " + this.clientKey_ + " with " + clientKey);
  3821.  
  3822.   this.clientKey_ = clientKey;
  3823.   this.clientKeyArray_ = this.base64_.decodeString(this.clientKey_);
  3824.   this.wrappedKey_ = wrappedKey;
  3825.  
  3826.   this.serializeKey_(this.clientKey_, this.wrappedKey_);
  3827. }
  3828.  
  3829. /**
  3830.  * Try to write the key to disk so we can fall back on it. Fail
  3831.  * silently if we cannot. The keys are serialized in protocol4 format.
  3832.  *
  3833.  * @returns Boolean indicating whether we succeeded in serializing
  3834.  */
  3835. PROT_UrlCryptoKeyManager.prototype.serializeKey_ = function() {
  3836.  
  3837.   var map = {};
  3838.   map[this.CLIENT_KEY_NAME] = this.clientKey_;
  3839.   map[this.WRAPPED_KEY_NAME] = this.wrappedKey_;
  3840.   
  3841.   try {  
  3842.  
  3843.     var appDir = new PROT_ApplicationDirectory();
  3844.     if (!appDir.exists())
  3845.       appDir.create();
  3846.     var keyfile = appDir.getAppDirFileInterface();
  3847.     keyfile.append(this.keyFilename_);
  3848.     G_FileWriter.writeAll(keyfile, (new G_Protocol4Parser).serialize(map));
  3849.     return true;
  3850.  
  3851.   } catch(e) {
  3852.  
  3853.     G_Error(this, "Failed to serialize new key: " + e);
  3854.     return false;
  3855.  
  3856.   }
  3857. }
  3858.  
  3859. /**
  3860.  * Invoked when we've received a protocol4 response to our getkey
  3861.  * request. Try to parse it and set this key as the new one if we can.
  3862.  *
  3863.  *  @param responseText String containing the protocol4 getkey response
  3864.  */ 
  3865. PROT_UrlCryptoKeyManager.prototype.onGetKeyResponse = function(responseText) {
  3866.  
  3867.   var response = (new G_Protocol4Parser).parse(responseText);
  3868.   var clientKey = response[this.CLIENT_KEY_NAME];
  3869.   var wrappedKey = response[this.WRAPPED_KEY_NAME];
  3870.  
  3871.   if (response && clientKey && wrappedKey) {
  3872.     G_Debug(this, "Got new key from: " + responseText);
  3873.     this.replaceKey_(clientKey, wrappedKey);
  3874.   } else {
  3875.     G_Debug(this, "Not a valid response for /getkey");
  3876.   }
  3877. }
  3878.  
  3879. /**
  3880.  * Attempt to read a key we've previously serialized from disk, so
  3881.  * that we can fall back on it in case we can't get one from the
  3882.  * server. If we get a key, only use it if we don't already have one
  3883.  * (i.e., if our startup HTTPS request died or hasn't yet completed).
  3884.  *
  3885.  * This method should be invoked early, like when the user's profile
  3886.  * becomes available.
  3887.  */ 
  3888. PROT_UrlCryptoKeyManager.prototype.maybeLoadOldKey = function() {
  3889.   
  3890.   var oldKey = null;
  3891.   try {  
  3892.     var appDir = new PROT_ApplicationDirectory();
  3893.     var keyfile = appDir.getAppDirFileInterface();
  3894.     keyfile.append(this.keyFilename_);
  3895.     if (keyfile.exists())
  3896.       oldKey = G_FileReader.readAll(keyfile);
  3897.   } catch(e) {
  3898.     G_Debug(this, "Caught " + e + " trying to read keyfile");
  3899.     return;
  3900.   }
  3901.    
  3902.   if (!oldKey) {
  3903.     G_Debug(this, "Couldn't find old key.");
  3904.     return;
  3905.   }
  3906.  
  3907.   oldKey = (new G_Protocol4Parser).parse(oldKey);
  3908.   var clientKey = oldKey[this.CLIENT_KEY_NAME];
  3909.   var wrappedKey = oldKey[this.WRAPPED_KEY_NAME];
  3910.  
  3911.   if (oldKey && clientKey && wrappedKey && !this.hasKey_()) {
  3912.     G_Debug(this, "Read old key from disk.");
  3913.     this.replaceKey_(clientKey, wrappedKey);
  3914.   }
  3915. }
  3916.  
  3917.  
  3918. /* ***** BEGIN LICENSE BLOCK *****
  3919.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3920.  *
  3921.  * The contents of this file are subject to the Mozilla Public License Version
  3922.  * 1.1 (the "License"); you may not use this file except in compliance with
  3923.  * the License. You may obtain a copy of the License at
  3924.  * http://www.mozilla.org/MPL/
  3925.  *
  3926.  * Software distributed under the License is distributed on an "AS IS" basis,
  3927.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3928.  * for the specific language governing rights and limitations under the
  3929.  * License.
  3930.  *
  3931.  * The Original Code is Google Safe Browsing.
  3932.  *
  3933.  * The Initial Developer of the Original Code is Google Inc.
  3934.  * Portions created by the Initial Developer are Copyright (C) 2006
  3935.  * the Initial Developer. All Rights Reserved.
  3936.  *
  3937.  * Contributor(s):
  3938.  *   Fritz Schneider <fritz@google.com> (original author)
  3939.  *
  3940.  * Alternatively, the contents of this file may be used under the terms of
  3941.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3942.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3943.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3944.  * of those above. If you wish to allow use of your version of this file only
  3945.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3946.  * use your version of this file under the terms of the MPL, indicate your
  3947.  * decision by deleting the provisions above and replace them with the notice
  3948.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3949.  * the provisions above, a recipient may use your version of this file under
  3950.  * the terms of any one of the MPL, the GPL or the LGPL.
  3951.  *
  3952.  * ***** END LICENSE BLOCK ***** */
  3953.  
  3954. // A simple class that encapsulates a request. You'll notice the
  3955. // style here is different from the rest of the extension; that's
  3956. // because this was re-used from really old code we had. At some
  3957. // point it might be nice to replace this with something better
  3958. // (e.g., something that has explicit onerror handler, ability
  3959. // to set headers, and so on).
  3960. //
  3961. // The only interesting thing here is its ability to strip cookies
  3962. // from the request.
  3963.  
  3964. /**
  3965.  * Because we might be in a component, we can't just assume that
  3966.  * XMLHttpRequest exists. So we use this tiny factory function to wrap the
  3967.  * XPCOM version.
  3968.  *
  3969.  * @return XMLHttpRequest object
  3970.  */
  3971. function PROT_NewXMLHttpRequest() {
  3972.   var Cc = Components.classes;
  3973.   var Ci = Components.interfaces;
  3974.   var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
  3975.                 .createInstance(Ci.nsIXMLHttpRequest);
  3976.   // Need the following so we get onerror/load/progresschange
  3977.   request.QueryInterface(Ci.nsIJSXMLHttpRequest);
  3978.   return request;
  3979. }
  3980.  
  3981. /**
  3982.  * A helper class that does HTTP GETs and calls back a function with
  3983.  * the content it receives. Asynchronous, so uses a closure for the
  3984.  * callback.
  3985.  *
  3986.  * @param opt_stripCookies Boolean indicating whether we should strip
  3987.  *                         cookies from this request
  3988.  * 
  3989.  * @constructor
  3990.  */
  3991. function PROT_XMLFetcher(opt_stripCookies) {
  3992.   this.debugZone = "xmlfetcher";
  3993.   this._request = PROT_NewXMLHttpRequest();
  3994.   this._stripCookies = !!opt_stripCookies;
  3995. }
  3996.  
  3997. PROT_XMLFetcher.prototype = {
  3998.   /**
  3999.    * Function that will be called back upon fetch completion.
  4000.    */
  4001.   _callback: null,
  4002.   
  4003.  
  4004.   /**
  4005.    * Fetches some content.
  4006.    * 
  4007.    * @param page URL to fetch
  4008.    * @param callback Function to call back when complete.
  4009.    */
  4010.   get: function(page, callback) {
  4011.     this._request.abort();                // abort() is asynchronous, so
  4012.     this._request = PROT_NewXMLHttpRequest();
  4013.     this._callback = callback;
  4014.     var asynchronous = true;
  4015.     this._request.open("GET", page, asynchronous);
  4016.  
  4017.     if (this._stripCookies)
  4018.       new PROT_CookieStripper(this._request.channel);
  4019.  
  4020.     // Create a closure
  4021.     var self = this;
  4022.     this._request.onreadystatechange = function() {
  4023.       self.readyStateChange(self);
  4024.     }
  4025.  
  4026.     this._request.send(null);
  4027.   },
  4028.  
  4029.   /**
  4030.    * Called periodically by the request to indicate some state change. 4
  4031.    * means content has been received.
  4032.    */
  4033.   readyStateChange: function(fetcher) {
  4034.     if (fetcher._request.readyState != 4)
  4035.       return;
  4036.  
  4037.     // If the request fails, on trunk we get status set to
  4038.     // NS_ERROR_NOT_AVAILABLE.  On 1.8.1 branch we get an exception
  4039.     // forwarded from nsIHttpChannel::GetResponseStatus.  To be consistent
  4040.     // between branch and trunk, we send back NS_ERROR_NOT_AVAILABLE for
  4041.     // http failures.
  4042.     var responseText = null;
  4043.     var status = Components.results.NS_ERROR_NOT_AVAILABLE;
  4044.     try {
  4045.       G_Debug(this, "xml fetch status code: \"" + 
  4046.               fetcher._request.status + "\"");
  4047.       status = fetcher._request.status;
  4048.       responseText = fetcher._request.responseText;
  4049.     } catch(e) {
  4050.       G_Debug(this, "Caught exception trying to read xmlhttprequest " +
  4051.               "status/response.");
  4052.       G_Debug(this, e);
  4053.     }
  4054.     if (fetcher._callback)
  4055.       fetcher._callback(responseText, status);
  4056.   }
  4057. };
  4058.  
  4059.  
  4060. /**
  4061.  * This class knows how to strip cookies from an HTTP request. It
  4062.  * listens for http-on-modify-request, and modifies the request
  4063.  * accordingly. We can't do this using xmlhttprequest.setHeader() or
  4064.  * nsIChannel.setRequestHeader() before send()ing because the cookie
  4065.  * service is called after send().
  4066.  * 
  4067.  * @param channel nsIChannel in which the request is happening
  4068.  * @constructor
  4069.  */
  4070. function PROT_CookieStripper(channel) {
  4071.   this.debugZone = "cookiestripper";
  4072.   this.topic_ = "http-on-modify-request";
  4073.   this.channel_ = channel;
  4074.  
  4075.   var Cc = Components.classes;
  4076.   var Ci = Components.interfaces;
  4077.   this.observerService_ = Cc["@mozilla.org/observer-service;1"]
  4078.                           .getService(Ci.nsIObserverService);
  4079.   this.observerService_.addObserver(this, this.topic_, false);
  4080.  
  4081.   // If the request doesn't issue, don't hang around forever
  4082.   var twentySeconds = 20 * 1000;
  4083.   this.alarm_ = new G_Alarm(BindToObject(this.stopObserving, this), 
  4084.                             twentySeconds);
  4085. }
  4086.  
  4087. /**
  4088.  * Invoked by the observerservice. See nsIObserve.
  4089.  */
  4090. PROT_CookieStripper.prototype.observe = function(subject, topic, data) {
  4091.   if (topic != this.topic_ || subject != this.channel_)
  4092.     return;
  4093.  
  4094.   G_Debug(this, "Stripping cookies for channel.");
  4095.  
  4096.   this.channel_.QueryInterface(Components.interfaces.nsIHttpChannel);
  4097.   this.channel_.setRequestHeader("Cookie", "", false /* replace, not add */);
  4098.   this.alarm_.cancel();
  4099.   this.stopObserving();
  4100. }
  4101.  
  4102. /**
  4103.  * Remove us from the observerservice
  4104.  */
  4105. PROT_CookieStripper.prototype.stopObserving = function() {
  4106.   G_Debug(this, "Removing observer");
  4107.   this.observerService_.removeObserver(this, this.topic_);
  4108.   this.channel_ = this.alarm_ = this.observerService_ = null;
  4109. }
  4110.  
  4111. /**
  4112.  * XPCOM cruft
  4113.  */
  4114. PROT_CookieStripper.prototype.QueryInterface = function(iid) {
  4115.   var Ci = Components.interfaces;
  4116.   if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserve))
  4117.     return this;
  4118.   throw Components.results.NS_ERROR_NO_INTERFACE;
  4119. }
  4120.  
  4121. //@line 65 "/cygdrive/K/tinderbuild/src/flock/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"
  4122.  
  4123. // Expose this whole component.
  4124. var lib = this;
  4125.  
  4126. function UrlClassifierLib() {
  4127.   this.wrappedJSObject = lib;
  4128. }
  4129.  
  4130. // Module object
  4131. function UrlClassifierLibMod() {
  4132.   this.firstTime = true;
  4133.   this.cid = Components.ID("{26a4a019-2827-4a89-a85c-5931a678823a}");
  4134.   this.progid = "@mozilla.org/url-classifier/jslib;1";
  4135. }
  4136.  
  4137. UrlClassifierLibMod.prototype.registerSelf = function(compMgr, fileSpec, loc, type) {
  4138.   if (this.firstTime) {
  4139.     this.firstTime = false;
  4140.     throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  4141.   }
  4142.   compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  4143.   compMgr.registerFactoryLocation(this.cid,
  4144.                                   "UrlClassifier JS Lib",
  4145.                                   this.progid,
  4146.                                   fileSpec,
  4147.                                   loc,
  4148.                                   type);
  4149. };
  4150.  
  4151. UrlClassifierLibMod.prototype.getClassObject = function(compMgr, cid, iid) {  
  4152.   if (!cid.equals(this.cid))
  4153.     throw Components.results.NS_ERROR_NO_INTERFACE;
  4154.   if (!iid.equals(Ci.nsIFactory))
  4155.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  4156.  
  4157.   return this.factory;
  4158. }
  4159.  
  4160. UrlClassifierLibMod.prototype.canUnload = function(compMgr) {
  4161.   return true;
  4162. }
  4163.  
  4164. UrlClassifierLibMod.prototype.factory = {
  4165.   createInstance: function(outer, iid) {
  4166.     if (outer != null)
  4167.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  4168.     return new UrlClassifierLib();
  4169.   }
  4170. };
  4171.  
  4172. var LibModInst = new UrlClassifierLibMod();
  4173.  
  4174. function NSGetModule(compMgr, fileSpec) {
  4175.   return LibModInst;
  4176. }
  4177.